From 30fdc8d841c9d24ac5f3d452b6ece84ee0ac991c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 8 Jun 2010 16:52:24 +0000 Subject: [PATCH] Initial checkin of lldb code from internal Apple repo. llvm-svn: 105619 --- lldb/docs/code-signing.txt | 47 + lldb/docs/code_signing.txt | 47 + lldb/include/lldb/API/LLDB.h | 49 + lldb/include/lldb/API/SBAddress.h | 75 + lldb/include/lldb/API/SBBlock.h | 44 + lldb/include/lldb/API/SBBreakpoint.h | 129 + lldb/include/lldb/API/SBBreakpointLocation.h | 73 + lldb/include/lldb/API/SBBroadcaster.h | 83 + lldb/include/lldb/API/SBCommandContext.h | 36 + lldb/include/lldb/API/SBCommandInterpreter.h | 105 + lldb/include/lldb/API/SBCommandReturnObject.h | 80 + lldb/include/lldb/API/SBCommunication.h | 97 + lldb/include/lldb/API/SBCompileUnit.h | 75 + lldb/include/lldb/API/SBDebugger.h | 148 + lldb/include/lldb/API/SBDefines.h | 62 + lldb/include/lldb/API/SBError.h | 102 + lldb/include/lldb/API/SBEvent.h | 89 + lldb/include/lldb/API/SBFileSpec.h | 83 + lldb/include/lldb/API/SBFrame.h | 130 + lldb/include/lldb/API/SBFunction.h | 55 + lldb/include/lldb/API/SBHostOS.h | 54 + lldb/include/lldb/API/SBInputReader.h | 100 + lldb/include/lldb/API/SBInstruction.h | 57 + lldb/include/lldb/API/SBInstructionList.h | 53 + lldb/include/lldb/API/SBLineEntry.h | 88 + lldb/include/lldb/API/SBListener.h | 119 + lldb/include/lldb/API/SBModule.h | 79 + lldb/include/lldb/API/SBProcess.h | 195 + lldb/include/lldb/API/SBSourceManager.h | 47 + lldb/include/lldb/API/SBStringList.h | 72 + lldb/include/lldb/API/SBSymbol.h | 55 + lldb/include/lldb/API/SBSymbolContext.h | 73 + lldb/include/lldb/API/SBTarget.h | 167 + lldb/include/lldb/API/SBThread.h | 152 + lldb/include/lldb/API/SBType.h | 31 + lldb/include/lldb/API/SBValue.h | 126 + lldb/include/lldb/API/SBValueList.h | 79 + lldb/include/lldb/Breakpoint/Breakpoint.h | 511 +++ lldb/include/lldb/Breakpoint/BreakpointID.h | 117 + .../lldb/Breakpoint/BreakpointIDList.h | 82 + lldb/include/lldb/Breakpoint/BreakpointList.h | 177 + .../lldb/Breakpoint/BreakpointLocation.h | 354 ++ .../Breakpoint/BreakpointLocationCollection.h | 187 + .../lldb/Breakpoint/BreakpointLocationList.h | 285 ++ .../lldb/Breakpoint/BreakpointOptions.h | 210 + .../lldb/Breakpoint/BreakpointResolver.h | 123 + .../Breakpoint/BreakpointResolverAddress.h | 68 + .../Breakpoint/BreakpointResolverFileLine.h | 65 + .../lldb/Breakpoint/BreakpointResolverName.h | 75 + lldb/include/lldb/Breakpoint/BreakpointSite.h | 258 ++ .../lldb/Breakpoint/BreakpointSiteList.h | 217 + lldb/include/lldb/Breakpoint/Stoppoint.h | 63 + .../Breakpoint/StoppointCallbackContext.h | 58 + .../lldb/Breakpoint/StoppointLocation.h | 111 + .../lldb/Breakpoint/WatchpointLocation.h | 69 + lldb/include/lldb/Core/Address.h | 425 ++ lldb/include/lldb/Core/AddressRange.h | 280 ++ lldb/include/lldb/Core/AddressResolver.h | 90 + .../lldb/Core/AddressResolverFileLine.h | 59 + lldb/include/lldb/Core/AddressResolverName.h | 67 + lldb/include/lldb/Core/ArchSpec.h | 326 ++ lldb/include/lldb/Core/Args.h | 368 ++ lldb/include/lldb/Core/Baton.h | 62 + lldb/include/lldb/Core/Broadcaster.h | 194 + lldb/include/lldb/Core/ClangForward.h | 103 + lldb/include/lldb/Core/Communication.h | 401 ++ lldb/include/lldb/Core/Connection.h | 176 + .../lldb/Core/ConnectionFileDescriptor.h | 74 + lldb/include/lldb/Core/ConstString.h | 396 ++ lldb/include/lldb/Core/DataBuffer.h | 94 + lldb/include/lldb/Core/DataBufferHeap.h | 136 + lldb/include/lldb/Core/DataBufferMemoryMap.h | 156 + lldb/include/lldb/Core/DataExtractor.h | 1124 +++++ lldb/include/lldb/Core/Debugger.h | 170 + lldb/include/lldb/Core/Disassembler.h | 138 + lldb/include/lldb/Core/Error.h | 291 ++ lldb/include/lldb/Core/Event.h | 180 + lldb/include/lldb/Core/FileSpec.h | 440 ++ lldb/include/lldb/Core/FileSpecList.h | 196 + lldb/include/lldb/Core/Flags.h | 153 + lldb/include/lldb/Core/IOStreamMacros.h | 38 + lldb/include/lldb/Core/InputReader.h | 114 + lldb/include/lldb/Core/Language.h | 149 + lldb/include/lldb/Core/Listener.h | 173 + lldb/include/lldb/Core/Log.h | 243 ++ lldb/include/lldb/Core/Mangled.h | 485 +++ lldb/include/lldb/Core/Module.h | 597 +++ lldb/include/lldb/Core/ModuleChild.h | 103 + lldb/include/lldb/Core/ModuleList.h | 339 ++ lldb/include/lldb/Core/Options.h | 296 ++ lldb/include/lldb/Core/PluginInterface.h | 46 + lldb/include/lldb/Core/PluginManager.h | 186 + lldb/include/lldb/Core/RegularExpression.h | 166 + lldb/include/lldb/Core/STLUtils.h | 104 + lldb/include/lldb/Core/Scalar.h | 302 ++ lldb/include/lldb/Core/SearchFilter.h | 326 ++ lldb/include/lldb/Core/Section.h | 231 ++ lldb/include/lldb/Core/SourceManager.h | 120 + lldb/include/lldb/Core/State.h | 43 + lldb/include/lldb/Core/Stream.h | 599 +++ lldb/include/lldb/Core/StreamFile.h | 76 + lldb/include/lldb/Core/StreamString.h | 61 + lldb/include/lldb/Core/StringList.h | 74 + lldb/include/lldb/Core/TTYState.h | 202 + lldb/include/lldb/Core/ThreadSafeSTLMap.h | 170 + lldb/include/lldb/Core/ThreadSafeValue.h | 96 + lldb/include/lldb/Core/Timer.h | 93 + lldb/include/lldb/Core/UUID.h | 80 + lldb/include/lldb/Core/UniqueCStringMap.h | 232 ++ lldb/include/lldb/Core/UserID.h | 123 + lldb/include/lldb/Core/VMRange.h | 165 + lldb/include/lldb/Core/Value.h | 172 + lldb/include/lldb/Core/ValueObject.h | 230 ++ lldb/include/lldb/Core/ValueObjectChild.h | 95 + lldb/include/lldb/Core/ValueObjectList.h | 74 + lldb/include/lldb/Core/ValueObjectRegister.h | 168 + lldb/include/lldb/Core/ValueObjectVariable.h | 70 + lldb/include/lldb/Core/dwarf.h | 589 +++ lldb/include/lldb/Expression/ClangASTSource.h | 67 + .../include/lldb/Expression/ClangExpression.h | 124 + .../lldb/Expression/ClangExpressionDeclMap.h | 71 + .../lldb/Expression/ClangExpressionVariable.h | 58 + lldb/include/lldb/Expression/ClangFunction.h | 241 ++ .../lldb/Expression/ClangStmtVisitor.h | 109 + .../include/lldb/Expression/DWARFExpression.h | 126 + .../lldb/Expression/RecordingMemoryManager.h | 154 + lldb/include/lldb/Host/Condition.h | 123 + lldb/include/lldb/Host/Endian.h | 19 + lldb/include/lldb/Host/Host.h | 281 ++ lldb/include/lldb/Host/Mutex.h | 242 ++ lldb/include/lldb/Host/Predicate.h | 411 ++ lldb/include/lldb/Host/Symbols.h | 37 + lldb/include/lldb/Host/TimeValue.h | 90 + lldb/include/lldb/Host/Types.h | 98 + .../lldb/Interpreter/CommandCompletions.h | 240 ++ .../include/lldb/Interpreter/CommandContext.h | 43 + .../lldb/Interpreter/CommandInterpreter.h | 264 ++ lldb/include/lldb/Interpreter/CommandObject.h | 194 + .../lldb/Interpreter/CommandObjectCrossref.h | 60 + .../lldb/Interpreter/CommandObjectMultiword.h | 73 + .../Interpreter/CommandObjectRegexCommand.h | 73 + .../lldb/Interpreter/CommandReturnObject.h | 90 + .../lldb/Interpreter/ScriptInterpreter.h | 103 + .../lldb/Interpreter/ScriptInterpreterNone.h | 35 + .../Interpreter/ScriptInterpreterPython.h | 86 + lldb/include/lldb/Interpreter/StateVariable.h | 145 + lldb/include/lldb/Symbol/Block.h | 721 ++++ lldb/include/lldb/Symbol/ClangASTContext.h | 417 ++ lldb/include/lldb/Symbol/CompileUnit.h | 395 ++ lldb/include/lldb/Symbol/DWARFCallFrameInfo.h | 312 ++ lldb/include/lldb/Symbol/Declaration.h | 204 + lldb/include/lldb/Symbol/Function.h | 587 +++ lldb/include/lldb/Symbol/LineEntry.h | 170 + lldb/include/lldb/Symbol/LineTable.h | 333 ++ lldb/include/lldb/Symbol/ObjectContainer.h | 232 ++ lldb/include/lldb/Symbol/ObjectFile.h | 309 ++ lldb/include/lldb/Symbol/Symbol.h | 181 + lldb/include/lldb/Symbol/SymbolContext.h | 328 ++ lldb/include/lldb/Symbol/SymbolContextScope.h | 79 + lldb/include/lldb/Symbol/SymbolFile.h | 95 + lldb/include/lldb/Symbol/SymbolVendor.h | 193 + lldb/include/lldb/Symbol/Symtab.h | 77 + lldb/include/lldb/Symbol/Type.h | 286 ++ lldb/include/lldb/Symbol/TypeList.h | 94 + lldb/include/lldb/Symbol/Variable.h | 123 + lldb/include/lldb/Symbol/VariableList.h | 74 + lldb/include/lldb/Target/ABI.h | 67 + lldb/include/lldb/Target/DynamicLoader.h | 154 + lldb/include/lldb/Target/ExecutionContext.h | 93 + .../lldb/Target/ExecutionContextScope.h | 71 + lldb/include/lldb/Target/ObjCObjectPrinter.h | 49 + lldb/include/lldb/Target/PathMappingList.h | 82 + lldb/include/lldb/Target/Process.h | 1415 +++++++ lldb/include/lldb/Target/RegisterContext.h | 172 + lldb/include/lldb/Target/StackFrame.h | 126 + lldb/include/lldb/Target/StackFrameList.h | 88 + lldb/include/lldb/Target/StackID.h | 65 + lldb/include/lldb/Target/Target.h | 324 ++ lldb/include/lldb/Target/TargetList.h | 206 + lldb/include/lldb/Target/Thread.h | 701 ++++ lldb/include/lldb/Target/ThreadList.h | 120 + lldb/include/lldb/Target/ThreadPlan.h | 358 ++ lldb/include/lldb/Target/ThreadPlanBase.h | 66 + .../lldb/Target/ThreadPlanCallFunction.h | 96 + lldb/include/lldb/Target/ThreadPlanContinue.h | 60 + .../lldb/Target/ThreadPlanRunToAddress.h | 78 + .../lldb/Target/ThreadPlanShouldStopHere.h | 94 + .../lldb/Target/ThreadPlanStepInRange.h | 76 + .../lldb/Target/ThreadPlanStepInstruction.h | 61 + lldb/include/lldb/Target/ThreadPlanStepOut.h | 72 + .../Target/ThreadPlanStepOverBreakpoint.h | 55 + .../lldb/Target/ThreadPlanStepOverRange.h | 56 + .../include/lldb/Target/ThreadPlanStepRange.h | 74 + .../lldb/Target/ThreadPlanStepThrough.h | 58 + .../include/lldb/Target/ThreadPlanStepUntil.h | 83 + lldb/include/lldb/Target/UnixSignals.h | 168 + lldb/include/lldb/Target/Unwind.h | 69 + lldb/include/lldb/lldb-defines.h | 88 + lldb/include/lldb/lldb-enumerations.h | 358 ++ lldb/include/lldb/lldb-forward-rtti.h | 76 + lldb/include/lldb/lldb-forward.h | 149 + lldb/include/lldb/lldb-include.h | 19 + lldb/include/lldb/lldb-private-interfaces.h | 37 + lldb/include/lldb/lldb-private-log.h | 83 + lldb/include/lldb/lldb-private.h | 77 + lldb/include/lldb/lldb-types.h | 168 + lldb/lldb.runcontext | 49 + lldb/lldb.xcodeproj/project.pbxproj | 3020 ++++++++++++++ .../lldb.xcworkspace/contents.xcworkspacedata | 18 + lldb/resources/LLDB-Info.plist | 24 + lldb/resources/lldb-framework-exports | 3 + lldb/scripts/Python/build-swig-Python.sh | 127 + .../Python/edit-swig-python-wrapper-file.py | 58 + .../scripts/Python/finish-swig-Python-LLDB.sh | 45 + lldb/scripts/build-llvm.pl | 409 ++ lldb/scripts/build-swig-wrapper-classes.sh | 85 + lldb/scripts/checkpoint-llvm.pl | 101 + lldb/scripts/finish-swig-wrapper-classes.sh | 71 + lldb/scripts/install-lldb.sh | 59 + lldb/scripts/lldb.swig | 149 + lldb/scripts/sed-sources | 252 ++ lldb/source/API/SBAddress.cpp | 118 + lldb/source/API/SBBlock.cpp | 47 + lldb/source/API/SBBreakpoint.cpp | 404 ++ lldb/source/API/SBBreakpointLocation.cpp | 162 + lldb/source/API/SBBroadcaster.cpp | 142 + lldb/source/API/SBCommandContext.cpp | 34 + lldb/source/API/SBCommandInterpreter.cpp | 193 + lldb/source/API/SBCommandReturnObject.cpp | 148 + lldb/source/API/SBCommunication.cpp | 194 + lldb/source/API/SBCompileUnit.cpp | 120 + lldb/source/API/SBDebugger.cpp | 569 +++ lldb/source/API/SBError.cpp | 179 + lldb/source/API/SBEvent.cpp | 176 + lldb/source/API/SBFileSpec.cpp | 133 + lldb/source/API/SBFrame.cpp | 394 ++ lldb/source/API/SBFunction.cpp | 64 + lldb/source/API/SBHostOS.cpp | 64 + lldb/source/API/SBInputReader.cpp | 169 + lldb/source/API/SBInstruction.cpp | 74 + lldb/source/API/SBInstructionList.cpp | 53 + lldb/source/API/SBLineEntry.cpp | 158 + lldb/source/API/SBListener.cpp | 299 ++ lldb/source/API/SBModule.cpp | 107 + lldb/source/API/SBProcess.cpp | 604 +++ lldb/source/API/SBSourceManager.cpp | 65 + lldb/source/API/SBStringList.cpp | 134 + lldb/source/API/SBSymbol.cpp | 64 + lldb/source/API/SBSymbolContext.cpp | 133 + lldb/source/API/SBTarget.cpp | 553 +++ lldb/source/API/SBThread.cpp | 551 +++ lldb/source/API/SBType.cpp | 23 + lldb/source/API/SBValue.cpp | 372 ++ lldb/source/API/SBValueList.cpp | 140 + lldb/source/Breakpoint/Breakpoint.cpp | 516 +++ lldb/source/Breakpoint/BreakpointID.cpp | 120 + lldb/source/Breakpoint/BreakpointIDList.cpp | 348 ++ lldb/source/Breakpoint/BreakpointList.cpp | 198 + lldb/source/Breakpoint/BreakpointLocation.cpp | 389 ++ .../BreakpointLocationCollection.cpp | 161 + .../Breakpoint/BreakpointLocationList.cpp | 305 ++ lldb/source/Breakpoint/BreakpointOptions.cpp | 180 + lldb/source/Breakpoint/BreakpointResolver.cpp | 60 + .../Breakpoint/BreakpointResolverAddress.cpp | 111 + .../Breakpoint/BreakpointResolverFileLine.cpp | 122 + .../Breakpoint/BreakpointResolverName.cpp | 252 ++ lldb/source/Breakpoint/BreakpointSite.cpp | 221 + lldb/source/Breakpoint/BreakpointSiteList.cpp | 229 ++ lldb/source/Breakpoint/Stoppoint.cpp | 46 + .../Breakpoint/StoppointCallbackContext.cpp | 38 + lldb/source/Breakpoint/StoppointLocation.cpp | 120 + lldb/source/Breakpoint/WatchpointLocation.cpp | 137 + lldb/source/Commands/CommandObjectAdd.cpp | 51 + lldb/source/Commands/CommandObjectAdd.h | 43 + lldb/source/Commands/CommandObjectAlias.cpp | 225 + lldb/source/Commands/CommandObjectAlias.h | 43 + lldb/source/Commands/CommandObjectAppend.cpp | 95 + lldb/source/Commands/CommandObjectAppend.h | 43 + lldb/source/Commands/CommandObjectApropos.cpp | 96 + lldb/source/Commands/CommandObjectApropos.h | 45 + lldb/source/Commands/CommandObjectArgs.cpp | 279 ++ lldb/source/Commands/CommandObjectArgs.h | 76 + .../Commands/CommandObjectBreakpoint.cpp | 953 +++++ .../source/Commands/CommandObjectBreakpoint.h | 235 ++ .../CommandObjectBreakpointCommand.cpp | 695 ++++ .../Commands/CommandObjectBreakpointCommand.h | 169 + lldb/source/Commands/CommandObjectCall.cpp | 307 ++ lldb/source/Commands/CommandObjectCall.h | 84 + lldb/source/Commands/CommandObjectDelete.cpp | 32 + lldb/source/Commands/CommandObjectDelete.h | 37 + .../Commands/CommandObjectDisassemble.cpp | 431 ++ .../Commands/CommandObjectDisassemble.h | 95 + .../Commands/CommandObjectExpression.cpp | 554 +++ .../source/Commands/CommandObjectExpression.h | 105 + lldb/source/Commands/CommandObjectFile.cpp | 170 + lldb/source/Commands/CommandObjectFile.h | 79 + lldb/source/Commands/CommandObjectFrame.cpp | 171 + lldb/source/Commands/CommandObjectFrame.h | 40 + lldb/source/Commands/CommandObjectHelp.cpp | 266 ++ lldb/source/Commands/CommandObjectHelp.h | 59 + lldb/source/Commands/CommandObjectImage.cpp | 1419 +++++++ lldb/source/Commands/CommandObjectImage.h | 44 + lldb/source/Commands/CommandObjectInfo.cpp | 32 + lldb/source/Commands/CommandObjectInfo.h | 37 + lldb/source/Commands/CommandObjectLog.cpp | 452 +++ lldb/source/Commands/CommandObjectLog.h | 48 + lldb/source/Commands/CommandObjectMemory.cpp | 680 ++++ lldb/source/Commands/CommandObjectMemory.h | 33 + lldb/source/Commands/CommandObjectProcess.cpp | 833 ++++ lldb/source/Commands/CommandObjectProcess.h | 37 + lldb/source/Commands/CommandObjectQuit.cpp | 48 + lldb/source/Commands/CommandObjectQuit.h | 51 + .../source/Commands/CommandObjectRegister.cpp | 231 ++ lldb/source/Commands/CommandObjectRegister.h | 44 + lldb/source/Commands/CommandObjectRemove.cpp | 89 + lldb/source/Commands/CommandObjectRemove.h | 44 + lldb/source/Commands/CommandObjectScript.cpp | 149 + lldb/source/Commands/CommandObjectScript.h | 58 + lldb/source/Commands/CommandObjectSelect.cpp | 32 + lldb/source/Commands/CommandObjectSelect.h | 37 + lldb/source/Commands/CommandObjectSet.cpp | 153 + lldb/source/Commands/CommandObjectSet.h | 44 + .../source/Commands/CommandObjectSettings.cpp | 62 + lldb/source/Commands/CommandObjectSettings.h | 44 + lldb/source/Commands/CommandObjectShow.cpp | 74 + lldb/source/Commands/CommandObjectShow.h | 44 + lldb/source/Commands/CommandObjectSource.cpp | 127 + lldb/source/Commands/CommandObjectSource.h | 48 + .../Commands/CommandObjectSourceFile.cpp | 206 + .../source/Commands/CommandObjectSourceFile.h | 80 + lldb/source/Commands/CommandObjectStatus.cpp | 97 + lldb/source/Commands/CommandObjectStatus.h | 44 + lldb/source/Commands/CommandObjectSyntax.cpp | 148 + lldb/source/Commands/CommandObjectSyntax.h | 51 + lldb/source/Commands/CommandObjectTarget.cpp | 430 ++ lldb/source/Commands/CommandObjectTarget.h | 41 + lldb/source/Commands/CommandObjectThread.cpp | 1277 ++++++ lldb/source/Commands/CommandObjectThread.h | 87 + .../Commands/CommandObjectTranslate.cpp | 75 + lldb/source/Commands/CommandObjectTranslate.h | 44 + lldb/source/Commands/CommandObjectUnalias.cpp | 87 + lldb/source/Commands/CommandObjectUnalias.h | 44 + .../source/Commands/CommandObjectVariable.cpp | 801 ++++ lldb/source/Commands/CommandObjectVariable.h | 43 + lldb/source/Core/Address.cpp | 875 ++++ lldb/source/Core/AddressRange.cpp | 222 + lldb/source/Core/AddressResolver.cpp | 67 + lldb/source/Core/AddressResolverFileLine.cpp | 100 + lldb/source/Core/AddressResolverName.cpp | 231 ++ lldb/source/Core/ArchSpec.cpp | 1681 ++++++++ lldb/source/Core/Args.cpp | 1179 ++++++ lldb/source/Core/Baton.cpp | 25 + lldb/source/Core/Broadcaster.cpp | 219 + lldb/source/Core/Communication.cpp | 363 ++ lldb/source/Core/Connection.cpp | 24 + lldb/source/Core/ConnectionFileDescriptor.cpp | 563 +++ lldb/source/Core/ConstString.cpp | 480 +++ lldb/source/Core/DataBufferHeap.cpp | 105 + lldb/source/Core/DataBufferMemoryMap.cpp | 214 + lldb/source/Core/DataExtractor.cpp | 1517 +++++++ lldb/source/Core/Debugger.cpp | 434 ++ lldb/source/Core/Disassembler.cpp | 299 ++ lldb/source/Core/DynamicLoader.cpp | 75 + lldb/source/Core/Error.cpp | 365 ++ lldb/source/Core/Event.cpp | 241 ++ lldb/source/Core/FileSpec.cpp | 580 +++ lldb/source/Core/FileSpecList.cpp | 228 ++ lldb/source/Core/Flags.cpp | 122 + lldb/source/Core/InputReader.cpp | 343 ++ lldb/source/Core/Language.cpp | 150 + lldb/source/Core/Listener.cpp | 480 +++ lldb/source/Core/Log.cpp | 590 +++ lldb/source/Core/Mangled.cpp | 733 ++++ lldb/source/Core/Module.cpp | 515 +++ lldb/source/Core/ModuleChild.cpp | 52 + lldb/source/Core/ModuleList.cpp | 626 +++ lldb/source/Core/Options.cpp | 700 ++++ lldb/source/Core/PluginManager.cpp | 1133 ++++++ lldb/source/Core/RegularExpression.cpp | 154 + lldb/source/Core/Scalar.cpp | 2084 ++++++++++ lldb/source/Core/SearchFilter.cpp | 435 ++ lldb/source/Core/Section.cpp | 791 ++++ lldb/source/Core/SourceManager.cpp | 305 ++ lldb/source/Core/State.cpp | 87 + lldb/source/Core/Stream.cpp | 776 ++++ lldb/source/Core/StreamFile.cpp | 132 + lldb/source/Core/StreamString.cpp | 81 + lldb/source/Core/StringList.cpp | 200 + lldb/source/Core/TTYState.cpp | 203 + lldb/source/Core/Timer.cpp | 235 ++ lldb/source/Core/UUID.cpp | 218 + lldb/source/Core/UserID.cpp | 73 + lldb/source/Core/VMRange.cpp | 100 + lldb/source/Core/Value.cpp | 803 ++++ lldb/source/Core/ValueObject.cpp | 678 ++++ lldb/source/Core/ValueObjectChild.cpp | 207 + lldb/source/Core/ValueObjectList.cpp | 119 + lldb/source/Core/ValueObjectRegister.cpp | 331 ++ lldb/source/Core/ValueObjectVariable.cpp | 167 + lldb/source/Expression/ClangASTSource.cpp | 101 + lldb/source/Expression/ClangExpression.cpp | 633 +++ .../Expression/ClangExpressionDeclMap.cpp | 246 ++ .../Expression/ClangExpressionVariable.cpp | 100 + lldb/source/Expression/ClangFunction.cpp | 671 +++ lldb/source/Expression/ClangStmtVisitor.cpp | 1032 +++++ lldb/source/Expression/DWARFExpression.cpp | 2589 ++++++++++++ .../Expression/RecordingMemoryManager.cpp | 131 + lldb/source/Host/macosx/Condition.cpp | 106 + lldb/source/Host/macosx/Host.mm | 803 ++++ lldb/source/Host/macosx/Mutex.cpp | 244 ++ lldb/source/Host/macosx/Symbols.cpp | 462 +++ lldb/source/Host/macosx/TimeValue.cpp | 179 + lldb/source/Host/macosx/cfcpp/CFCBundle.cpp | 83 + lldb/source/Host/macosx/cfcpp/CFCBundle.h | 47 + lldb/source/Host/macosx/cfcpp/CFCData.cpp | 82 + lldb/source/Host/macosx/cfcpp/CFCData.h | 35 + .../Host/macosx/cfcpp/CFCMutableArray.cpp | 123 + .../Host/macosx/cfcpp/CFCMutableArray.h | 34 + .../macosx/cfcpp/CFCMutableDictionary.cpp | 491 +++ .../Host/macosx/cfcpp/CFCMutableDictionary.h | 77 + .../Host/macosx/cfcpp/CFCMutableSet.cpp | 114 + lldb/source/Host/macosx/cfcpp/CFCMutableSet.h | 53 + lldb/source/Host/macosx/cfcpp/CFCReleaser.h | 155 + lldb/source/Host/macosx/cfcpp/CFCString.cpp | 195 + lldb/source/Host/macosx/cfcpp/CFCString.h | 41 + .../Host/macosx/cfcpp/CoreFoundationCPP.h | 30 + .../source/Interpreter/CommandCompletions.cpp | 414 ++ lldb/source/Interpreter/CommandContext.cpp | 77 + .../source/Interpreter/CommandInterpreter.cpp | 1300 ++++++ lldb/source/Interpreter/CommandObject.cpp | 448 ++ .../Interpreter/CommandObjectCrossref.cpp | 92 + .../Interpreter/CommandObjectMultiword.cpp | 263 ++ .../Interpreter/CommandObjectRegexCommand.cpp | 123 + .../Interpreter/CommandReturnObject.cpp | 175 + lldb/source/Interpreter/ScriptInterpreter.cpp | 66 + .../Interpreter/ScriptInterpreterNone.cpp | 38 + .../Interpreter/ScriptInterpreterPython.cpp | 830 ++++ lldb/source/Interpreter/StateVariable.cpp | 320 ++ .../Interpreter/embedded_interpreter.py | 90 + .../ABI/MacOSX-i386/ABIMacOSX_i386.cpp | 578 +++ .../Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h | 93 + .../ABI/SysV-x86_64/ABISysV_x86_64.cpp | 412 ++ .../Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h | 92 + .../Disassembler/llvm/DisassemblerLLVM.cpp | 468 +++ .../Disassembler/llvm/DisassemblerLLVM.h | 111 + .../MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp | 1129 +++++ .../MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h | 360 ++ .../DynamicLoaderMacOSXDYLDLog.cpp | 72 + .../MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h | 34 + .../MacOSX-DYLD/ObjCTrampolineHandler.cpp | 328 ++ .../MacOSX-DYLD/ObjCTrampolineHandler.h | 133 + .../ThreadPlanStepThroughObjCTrampoline.cpp | 151 + .../ThreadPlanStepThroughObjCTrampoline.h | 94 + .../BSD-Archive/ObjectContainerBSDArchive.cpp | 428 ++ .../BSD-Archive/ObjectContainerBSDArchive.h | 183 + .../ObjectContainerUniversalMachO.cpp | 259 ++ .../ObjectContainerUniversalMachO.h | 103 + .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 929 +++++ .../Plugins/ObjectFile/ELF/ObjectFileELF.h | 197 + lldb/source/Plugins/ObjectFile/ELF/elf.h | 240 ++ .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 1311 ++++++ .../ObjectFile/Mach-O/ObjectFileMachO.h | 131 + .../Process/MacOSX-User/scripts/cc-swig | 47 + .../Process/MacOSX-User/scripts/config.pl | 71 + .../MacOSX-User/scripts/test-ProcessDebug.pl | 409 ++ .../source/MacOSX/MachException.cpp | 575 +++ .../MacOSX-User/source/MacOSX/MachException.h | 148 + .../MacOSX-User/source/MacOSX/MachTask.cpp | 674 +++ .../MacOSX-User/source/MacOSX/MachTask.h | 138 + .../source/MacOSX/MachThreadContext.h | 48 + .../source/MacOSX/MachThreadContext_arm.cpp | 1884 +++++++++ .../source/MacOSX/MachThreadContext_arm.h | 63 + .../source/MacOSX/MachThreadContext_i386.cpp | 245 ++ .../source/MacOSX/MachThreadContext_i386.h | 57 + .../MacOSX/MachThreadContext_x86_64.cpp | 255 ++ .../source/MacOSX/MachThreadContext_x86_64.h | 72 + .../source/MacOSX/MachVMMemory.cpp | 195 + .../MacOSX-User/source/MacOSX/MachVMMemory.h | 36 + .../source/MacOSX/MachVMRegion.cpp | 183 + .../MacOSX-User/source/MacOSX/MachVMRegion.h | 63 + .../source/MacOSX/ProcessControl-mig.defs | 16 + .../MacOSX-User/source/ProcessMacOSX.cpp | 2228 ++++++++++ .../MacOSX-User/source/ProcessMacOSX.h | 490 +++ .../MacOSX-User/source/ProcessMacOSXLog.cpp | 124 + .../MacOSX-User/source/ProcessMacOSXLog.h | 62 + .../source/ProcessMacOSXRemote.cpp | 1819 +++++++++ .../MacOSX-User/source/ProcessMacOSXRemote.h | 206 + .../source/RegisterContextMach_arm.cpp | 1448 +++++++ .../source/RegisterContextMach_arm.h | 302 ++ .../source/RegisterContextMach_i386.cpp | 1202 ++++++ .../source/RegisterContextMach_i386.h | 256 ++ .../source/RegisterContextMach_x86_64.cpp | 1328 ++++++ .../source/RegisterContextMach_x86_64.h | 261 ++ .../MacOSX-User/source/ThreadMacOSX.cpp | 769 ++++ .../Process/MacOSX-User/source/ThreadMacOSX.h | 159 + .../Utility/LibUnwindRegisterContext.cpp | 327 ++ .../Utility/LibUnwindRegisterContext.h | 83 + .../Utility/MacOSXLibunwindCallbacks.cpp | 306 ++ .../Utility/MacOSXLibunwindCallbacks.h | 22 + .../RegisterContextMacOSXFrameBackchain.cpp | 255 ++ .../RegisterContextMacOSXFrameBackchain.h | 83 + .../Process/Utility/UnwindLibUnwind.cpp | 73 + .../Plugins/Process/Utility/UnwindLibUnwind.h | 66 + .../Utility/UnwindMacOSXFrameBackchain.cpp | 243 ++ .../Utility/UnwindMacOSXFrameBackchain.h | 77 + .../Utility/libunwind/include/libunwind.h | 509 +++ .../include/mach-o/compact_unwind_encoding.h | 212 + .../Utility/libunwind/include/unwind.h | 213 + .../Utility/libunwind/src/AddressSpace.hpp | 456 +++ .../libunwind/src/ArchDefaultUnwinder.hpp | 115 + .../libunwind/src/AssemblyInstructions.hpp | 147 + .../Utility/libunwind/src/AssemblyParser.hpp | 409 ++ .../Utility/libunwind/src/CompactUnwinder.hpp | 1019 +++++ .../libunwind/src/DwarfInstructions.hpp | 1686 ++++++++ .../Utility/libunwind/src/DwarfParser.hpp | 869 ++++ .../Utility/libunwind/src/FileAbstraction.hpp | 135 + .../Utility/libunwind/src/InternalMacros.h | 89 + .../Utility/libunwind/src/Registers.hpp | 985 +++++ .../Process/Utility/libunwind/src/Registers.s | 261 ++ .../src/RemoteDebuggerDummyUnwinder.hpp | 88 + .../Utility/libunwind/src/RemoteProcInfo.hpp | 977 +++++ .../libunwind/src/RemoteRegisterMap.hpp | 405 ++ .../libunwind/src/RemoteUnwindProfile.h | 85 + .../Utility/libunwind/src/Unwind-sjlj.c | 466 +++ .../Utility/libunwind/src/UnwindCursor.hpp | 1307 ++++++ .../libunwind/src/UnwindLevel1-gcc-ext.c | 282 ++ .../Utility/libunwind/src/UnwindLevel1.c | 443 ++ .../Process/Utility/libunwind/src/dwarf2.h | 245 ++ .../Utility/libunwind/src/libunwind_priv.h | 35 + .../Utility/libunwind/src/libuwind.cxx | 421 ++ .../Utility/libunwind/src/unw_getcontext.s | 229 ++ .../gdb-remote/GDBRemoteCommunication.cpp | 813 ++++ .../gdb-remote/GDBRemoteCommunication.h | 270 ++ .../gdb-remote/GDBRemoteRegisterContext.cpp | 508 +++ .../gdb-remote/GDBRemoteRegisterContext.h | 250 ++ .../Plugins/Process/gdb-remote/GDBServer.cpp | 1148 ++++++ .../Process/gdb-remote/GDBServerLog.cpp | 80 + .../Plugins/Process/gdb-remote/GDBServerLog.h | 55 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 2272 +++++++++++ .../Process/gdb-remote/ProcessGDBRemote.h | 404 ++ .../gdb-remote/ProcessGDBRemoteLog.cpp | 121 + .../Process/gdb-remote/ProcessGDBRemoteLog.h | 53 + .../Process/gdb-remote/ThreadGDBRemote.cpp | 296 ++ .../Process/gdb-remote/ThreadGDBRemote.h | 156 + .../DWARF/DWARFAbbreviationDeclaration.cpp | 211 + .../DWARF/DWARFAbbreviationDeclaration.h | 77 + .../Plugins/SymbolFile/DWARF/DWARFAttribute.h | 45 + .../SymbolFile/DWARF/DWARFCompileUnit.cpp | 770 ++++ .../SymbolFile/DWARF/DWARFCompileUnit.h | 168 + .../SymbolFile/DWARF/DWARFDIECollection.cpp | 56 + .../SymbolFile/DWARF/DWARFDIECollection.h | 48 + .../SymbolFile/DWARF/DWARFDebugAbbrev.cpp | 202 + .../SymbolFile/DWARF/DWARFDebugAbbrev.h | 74 + .../SymbolFile/DWARF/DWARFDebugArangeSet.cpp | 274 ++ .../SymbolFile/DWARF/DWARFDebugArangeSet.h | 70 + .../SymbolFile/DWARF/DWARFDebugAranges.cpp | 343 ++ .../SymbolFile/DWARF/DWARFDebugAranges.h | 98 + .../SymbolFile/DWARF/DWARFDebugInfo.cpp | 1206 ++++++ .../Plugins/SymbolFile/DWARF/DWARFDebugInfo.h | 86 + .../SymbolFile/DWARF/DWARFDebugInfoEntry.cpp | 1929 +++++++++ .../SymbolFile/DWARF/DWARFDebugInfoEntry.h | 320 ++ .../SymbolFile/DWARF/DWARFDebugLine.cpp | 1410 +++++++ .../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 | 297 ++ .../SymbolFile/DWARF/DWARFDebugPubnames.h | 38 + .../DWARF/DWARFDebugPubnamesSet.cpp | 166 + .../SymbolFile/DWARF/DWARFDebugPubnamesSet.h | 91 + .../SymbolFile/DWARF/DWARFDebugRanges.cpp | 275 ++ .../SymbolFile/DWARF/DWARFDebugRanges.h | 89 + .../Plugins/SymbolFile/DWARF/DWARFDefines.c | 2224 ++++++++++ .../Plugins/SymbolFile/DWARF/DWARFDefines.h | 252 ++ .../SymbolFile/DWARF/DWARFFormValue.cpp | 571 +++ .../Plugins/SymbolFile/DWARF/DWARFFormValue.h | 81 + .../DWARF/DWARFLocationDescription.cpp | 172 + .../DWARF/DWARFLocationDescription.h | 24 + .../SymbolFile/DWARF/DWARFLocationList.cpp | 89 + .../SymbolFile/DWARF/DWARFLocationList.h | 34 + .../SymbolFile/DWARF/LogChannelDWARF.cpp | 207 + .../SymbolFile/DWARF/LogChannelDWARF.h | 91 + .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 3615 +++++++++++++++++ .../SymbolFile/DWARF/SymbolFileDWARF.h | 331 ++ .../DWARF/SymbolFileDWARFDebugMap.cpp | 873 ++++ .../DWARF/SymbolFileDWARFDebugMap.h | 186 + .../SymbolFile/Symtab/SymbolFileSymtab.cpp | 401 ++ .../SymbolFile/Symtab/SymbolFileSymtab.h | 136 + .../MacOSX/SymbolVendorMacOSX.cpp | 339 ++ .../SymbolVendor/MacOSX/SymbolVendorMacOSX.h | 71 + lldb/source/Symbol/Block.cpp | 641 +++ lldb/source/Symbol/ClangASTContext.cpp | 2552 ++++++++++++ lldb/source/Symbol/CompileUnit.cpp | 366 ++ lldb/source/Symbol/DWARFCallFrameInfo.cpp | 1344 ++++++ lldb/source/Symbol/Declaration.cpp | 172 + lldb/source/Symbol/Function.cpp | 432 ++ lldb/source/Symbol/LineEntry.cpp | 237 ++ lldb/source/Symbol/LineTable.cpp | 332 ++ lldb/source/Symbol/ObjectFile.cpp | 92 + lldb/source/Symbol/Symbol.cpp | 463 +++ lldb/source/Symbol/SymbolContext.cpp | 424 ++ lldb/source/Symbol/SymbolFile.cpp | 50 + lldb/source/Symbol/SymbolVendor.mm | 386 ++ lldb/source/Symbol/Symtab.cpp | 596 +++ lldb/source/Symbol/Type.cpp | 1531 +++++++ lldb/source/Symbol/TypeList.cpp | 239 ++ lldb/source/Symbol/Variable.cpp | 167 + lldb/source/Symbol/VariableList.cpp | 116 + lldb/source/Target/ABI.cpp | 47 + lldb/source/Target/ExecutionContext.cpp | 107 + lldb/source/Target/ObjCObjectPrinter.cpp | 120 + lldb/source/Target/PathMappingList.cpp | 125 + lldb/source/Target/Process.cpp | 1876 +++++++++ lldb/source/Target/RegisterContext.cpp | 238 ++ lldb/source/Target/StackFrame.cpp | 393 ++ lldb/source/Target/StackFrameList.cpp | 135 + lldb/source/Target/StackID.cpp | 110 + lldb/source/Target/Target.cpp | 707 ++++ lldb/source/Target/TargetList.cpp | 342 ++ lldb/source/Target/Thread.cpp | 1121 +++++ lldb/source/Target/ThreadList.cpp | 460 +++ lldb/source/Target/ThreadPlan.cpp | 185 + lldb/source/Target/ThreadPlanBase.cpp | 202 + lldb/source/Target/ThreadPlanCallFunction.cpp | 250 ++ lldb/source/Target/ThreadPlanContinue.cpp | 120 + lldb/source/Target/ThreadPlanRunToAddress.cpp | 176 + .../Target/ThreadPlanShouldStopHere.cpp | 53 + lldb/source/Target/ThreadPlanStepInRange.cpp | 154 + .../Target/ThreadPlanStepInstruction.cpp | 191 + lldb/source/Target/ThreadPlanStepOut.cpp | 228 ++ .../Target/ThreadPlanStepOverBreakpoint.cpp | 130 + .../source/Target/ThreadPlanStepOverRange.cpp | 119 + lldb/source/Target/ThreadPlanStepRange.cpp | 263 ++ lldb/source/Target/ThreadPlanStepThrough.cpp | 137 + lldb/source/Target/ThreadPlanStepUntil.cpp | 360 ++ lldb/source/Target/UnixSignals.cpp | 310 ++ lldb/source/Utility/ARM_DWARF_Registers.h | 190 + lldb/source/Utility/ARM_GCC_Registers.h | 35 + lldb/source/Utility/PseudoTerminal.cpp | 336 ++ lldb/source/Utility/PseudoTerminal.h | 267 ++ lldb/source/Utility/StringExtractor.cpp | 360 ++ lldb/source/Utility/StringExtractor.h | 126 + .../Utility/StringExtractorGDBRemote.cpp | 89 + .../source/Utility/StringExtractorGDBRemote.h | 73 + lldb/source/lldb-log.cpp | 190 + lldb/source/lldb.cpp | 107 + lldb/test/Makefile | 21 + lldb/test/array_types/Makefile | 125 + lldb/test/array_types/cmds.txt | 3 + lldb/test/array_types/main.c | 51 + lldb/test/bitfields/Makefile | 125 + lldb/test/bitfields/main.c | 44 + lldb/test/class_types/Makefile | 125 + lldb/test/class_types/cmds.txt | 3 + lldb/test/class_types/main.cpp | 106 + lldb/test/dead-strip/Makefile | 126 + lldb/test/dead-strip/cmds.txt | 4 + lldb/test/dead-strip/main.c | 53 + lldb/test/enum_types/Makefile | 125 + lldb/test/enum_types/main.c | 29 + lldb/test/function_types/Makefile | 125 + lldb/test/function_types/main.c | 22 + lldb/test/global_variables/Makefile | 142 + lldb/test/global_variables/a.c | 10 + lldb/test/global_variables/cmds.txt | 3 + lldb/test/global_variables/main.c | 21 + lldb/test/load_unload/Makefile | 33 + lldb/test/load_unload/a.c | 15 + lldb/test/load_unload/b.c | 13 + lldb/test/load_unload/c.c | 13 + lldb/test/load_unload/cmds.txt | 2 + lldb/test/load_unload/main.c | 72 + lldb/test/namespace/Makefile | 125 + lldb/test/namespace/cmds.txt | 3 + lldb/test/namespace/main.cpp | 69 + lldb/test/order/Makefile | 127 + lldb/test/order/cmds.txt | 3 + lldb/test/order/main.c | 54 + lldb/test/order/order-file | 4 + lldb/test/print-obj/Makefile | 125 + lldb/test/print-obj/blocked.m | 73 + lldb/test/set_values/Makefile | 125 + lldb/test/set_values/main.c | 116 + lldb/test/signed_types/Makefile | 125 + lldb/test/signed_types/main.cpp | 31 + lldb/test/stl/Makefile | 125 + lldb/test/stl/cmds.txt | 3 + lldb/test/stl/main.cpp | 15 + lldb/test/struct_types/Makefile | 125 + lldb/test/struct_types/cmds.txt | 3 + lldb/test/struct_types/main.c | 23 + lldb/test/tester.py | 107 + lldb/test/threads/Makefile | 125 + lldb/test/threads/main.cpp | 129 + lldb/test/unsigned_types/Makefile | 125 + lldb/test/unsigned_types/main.cpp | 22 + lldb/tools/debugserver/debugnub-exports | 2 + .../debugserver.xcodeproj/project.pbxproj | 604 +++ .../resources/lldb-debugserver-Info.plist | 21 + .../lldb-debugserver-entitlements.plist | 8 + .../debugserver/scripts/dbgnub-config.pl | 71 + lldb/tools/debugserver/source/ChangeLog | 1515 +++++++ lldb/tools/debugserver/source/DNB.cpp | 1996 +++++++++ lldb/tools/debugserver/source/DNB.h | 141 + lldb/tools/debugserver/source/DNBArch.h | 61 + .../debugserver/source/DNBBreakpoint.cpp | 303 ++ lldb/tools/debugserver/source/DNBBreakpoint.h | 159 + lldb/tools/debugserver/source/DNBDataRef.cpp | 485 +++ lldb/tools/debugserver/source/DNBDataRef.h | 111 + lldb/tools/debugserver/source/DNBDefs.h | 331 ++ lldb/tools/debugserver/source/DNBError.cpp | 108 + lldb/tools/debugserver/source/DNBError.h | 96 + lldb/tools/debugserver/source/DNBLog.cpp | 309 ++ lldb/tools/debugserver/source/DNBLog.h | 96 + .../debugserver/source/DNBRegisterInfo.cpp | 220 + .../debugserver/source/DNBRegisterInfo.h | 31 + .../debugserver/source/DNBRuntimeAction.h | 25 + .../source/DNBThreadResumeActions.cpp | 116 + .../source/DNBThreadResumeActions.h | 95 + lldb/tools/debugserver/source/DNBTimer.h | 162 + .../debugserver/source/FunctionProfiler.cpp | 288 ++ .../debugserver/source/FunctionProfiler.h | 70 + .../debugserver/source/MacOSX/CFBundle.cpp | 87 + .../debugserver/source/MacOSX/CFBundle.h | 37 + .../debugserver/source/MacOSX/CFData.cpp | 85 + lldb/tools/debugserver/source/MacOSX/CFData.h | 39 + .../debugserver/source/MacOSX/CFString.cpp | 201 + .../debugserver/source/MacOSX/CFString.h | 43 + .../tools/debugserver/source/MacOSX/CFUtils.h | 81 + .../debugserver/source/MacOSX/MachDYLD.cpp | 679 ++++ .../debugserver/source/MacOSX/MachDYLD.h | 145 + .../source/MacOSX/MachException.cpp | 533 +++ .../debugserver/source/MacOSX/MachException.h | 147 + .../debugserver/source/MacOSX/MachProcess.cpp | 2008 +++++++++ .../debugserver/source/MacOSX/MachProcess.h | 263 ++ .../debugserver/source/MacOSX/MachTask.cpp | 660 +++ .../debugserver/source/MacOSX/MachTask.h | 91 + .../debugserver/source/MacOSX/MachThread.cpp | 745 ++++ .../debugserver/source/MacOSX/MachThread.h | 124 + .../source/MacOSX/MachThreadList.cpp | 432 ++ .../source/MacOSX/MachThreadList.h | 71 + .../source/MacOSX/MachVMMemory.cpp | 186 + .../debugserver/source/MacOSX/MachVMMemory.h | 40 + .../source/MacOSX/MachVMRegion.cpp | 179 + .../debugserver/source/MacOSX/MachVMRegion.h | 67 + .../source/MacOSX/arm/DNBArchImpl.cpp | 2610 ++++++++++++ .../source/MacOSX/arm/DNBArchImpl.h | 217 + .../debugserver/source/MacOSX/dbgnub-mig.defs | 16 + .../source/MacOSX/i386/DNBArchImplI386.cpp | 879 ++++ .../source/MacOSX/i386/DNBArchImplI386.h | 196 + .../source/MacOSX/ppc/DNBArchImpl.cpp | 569 +++ .../source/MacOSX/ppc/DNBArchImpl.h | 180 + .../MacOSX/x86_64/DNBArchImplX86_64.cpp | 1035 +++++ .../source/MacOSX/x86_64/DNBArchImplX86_64.h | 199 + .../debugserver/source/PThreadCondition.h | 53 + .../tools/debugserver/source/PThreadEvent.cpp | 227 ++ lldb/tools/debugserver/source/PThreadEvent.h | 59 + .../tools/debugserver/source/PThreadMutex.cpp | 84 + lldb/tools/debugserver/source/PThreadMutex.h | 148 + .../debugserver/source/ProfileObjectiveC.cpp | 393 ++ .../debugserver/source/ProfileObjectiveC.h | 82 + .../debugserver/source/PseudoTerminal.cpp | 226 ++ .../tools/debugserver/source/PseudoTerminal.h | 94 + lldb/tools/debugserver/source/RNBContext.cpp | 230 ++ lldb/tools/debugserver/source/RNBContext.h | 123 + lldb/tools/debugserver/source/RNBDefs.h | 78 + lldb/tools/debugserver/source/RNBRemote.cpp | 3187 +++++++++++++++ lldb/tools/debugserver/source/RNBRemote.h | 309 ++ lldb/tools/debugserver/source/RNBServices.cpp | 145 + lldb/tools/debugserver/source/RNBServices.h | 29 + lldb/tools/debugserver/source/RNBSocket.cpp | 251 ++ lldb/tools/debugserver/source/RNBSocket.h | 65 + lldb/tools/debugserver/source/SysSignal.cpp | 66 + lldb/tools/debugserver/source/SysSignal.h | 23 + lldb/tools/debugserver/source/TTYState.cpp | 122 + lldb/tools/debugserver/source/TTYState.h | 61 + .../com.apple.debugserver.applist.plist | 16 + .../source/com.apple.debugserver.plist | 15 + .../source/debugserver-entitlements.plist | 14 + lldb/tools/debugserver/source/debugserver.cpp | 1219 ++++++ lldb/tools/driver/Driver.cpp | 1265 ++++++ lldb/tools/driver/Driver.h | 156 + lldb/tools/driver/IOChannel.cpp | 449 ++ lldb/tools/driver/IOChannel.h | 113 + lldb/tools/driver/lldb-Info.plist | 21 + lldb/www/content.css | 25 + lldb/www/index.html | 197 + lldb/www/menu.css | 39 + lldb/www/menu.html.incl | 20 + 790 files changed, 216273 insertions(+) create mode 100644 lldb/docs/code-signing.txt create mode 100644 lldb/docs/code_signing.txt create mode 100644 lldb/include/lldb/API/LLDB.h create mode 100644 lldb/include/lldb/API/SBAddress.h create mode 100644 lldb/include/lldb/API/SBBlock.h create mode 100644 lldb/include/lldb/API/SBBreakpoint.h create mode 100644 lldb/include/lldb/API/SBBreakpointLocation.h create mode 100644 lldb/include/lldb/API/SBBroadcaster.h create mode 100644 lldb/include/lldb/API/SBCommandContext.h create mode 100644 lldb/include/lldb/API/SBCommandInterpreter.h create mode 100644 lldb/include/lldb/API/SBCommandReturnObject.h create mode 100644 lldb/include/lldb/API/SBCommunication.h create mode 100644 lldb/include/lldb/API/SBCompileUnit.h create mode 100644 lldb/include/lldb/API/SBDebugger.h create mode 100644 lldb/include/lldb/API/SBDefines.h create mode 100644 lldb/include/lldb/API/SBError.h create mode 100644 lldb/include/lldb/API/SBEvent.h create mode 100644 lldb/include/lldb/API/SBFileSpec.h create mode 100644 lldb/include/lldb/API/SBFrame.h create mode 100644 lldb/include/lldb/API/SBFunction.h create mode 100644 lldb/include/lldb/API/SBHostOS.h create mode 100644 lldb/include/lldb/API/SBInputReader.h create mode 100644 lldb/include/lldb/API/SBInstruction.h create mode 100644 lldb/include/lldb/API/SBInstructionList.h create mode 100644 lldb/include/lldb/API/SBLineEntry.h create mode 100644 lldb/include/lldb/API/SBListener.h create mode 100644 lldb/include/lldb/API/SBModule.h create mode 100644 lldb/include/lldb/API/SBProcess.h create mode 100644 lldb/include/lldb/API/SBSourceManager.h create mode 100644 lldb/include/lldb/API/SBStringList.h create mode 100644 lldb/include/lldb/API/SBSymbol.h create mode 100644 lldb/include/lldb/API/SBSymbolContext.h create mode 100644 lldb/include/lldb/API/SBTarget.h create mode 100644 lldb/include/lldb/API/SBThread.h create mode 100644 lldb/include/lldb/API/SBType.h create mode 100644 lldb/include/lldb/API/SBValue.h create mode 100644 lldb/include/lldb/API/SBValueList.h create mode 100644 lldb/include/lldb/Breakpoint/Breakpoint.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointID.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointIDList.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointList.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointLocation.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointLocationList.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointOptions.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointResolver.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointResolverName.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointSite.h create mode 100644 lldb/include/lldb/Breakpoint/BreakpointSiteList.h create mode 100644 lldb/include/lldb/Breakpoint/Stoppoint.h create mode 100644 lldb/include/lldb/Breakpoint/StoppointCallbackContext.h create mode 100644 lldb/include/lldb/Breakpoint/StoppointLocation.h create mode 100644 lldb/include/lldb/Breakpoint/WatchpointLocation.h create mode 100644 lldb/include/lldb/Core/Address.h create mode 100644 lldb/include/lldb/Core/AddressRange.h create mode 100644 lldb/include/lldb/Core/AddressResolver.h create mode 100644 lldb/include/lldb/Core/AddressResolverFileLine.h create mode 100644 lldb/include/lldb/Core/AddressResolverName.h create mode 100644 lldb/include/lldb/Core/ArchSpec.h create mode 100644 lldb/include/lldb/Core/Args.h create mode 100644 lldb/include/lldb/Core/Baton.h create mode 100644 lldb/include/lldb/Core/Broadcaster.h create mode 100644 lldb/include/lldb/Core/ClangForward.h create mode 100644 lldb/include/lldb/Core/Communication.h create mode 100644 lldb/include/lldb/Core/Connection.h create mode 100644 lldb/include/lldb/Core/ConnectionFileDescriptor.h create mode 100644 lldb/include/lldb/Core/ConstString.h create mode 100644 lldb/include/lldb/Core/DataBuffer.h create mode 100644 lldb/include/lldb/Core/DataBufferHeap.h create mode 100644 lldb/include/lldb/Core/DataBufferMemoryMap.h create mode 100644 lldb/include/lldb/Core/DataExtractor.h create mode 100644 lldb/include/lldb/Core/Debugger.h create mode 100644 lldb/include/lldb/Core/Disassembler.h create mode 100644 lldb/include/lldb/Core/Error.h create mode 100644 lldb/include/lldb/Core/Event.h create mode 100644 lldb/include/lldb/Core/FileSpec.h create mode 100644 lldb/include/lldb/Core/FileSpecList.h create mode 100644 lldb/include/lldb/Core/Flags.h create mode 100644 lldb/include/lldb/Core/IOStreamMacros.h create mode 100644 lldb/include/lldb/Core/InputReader.h create mode 100644 lldb/include/lldb/Core/Language.h create mode 100644 lldb/include/lldb/Core/Listener.h create mode 100644 lldb/include/lldb/Core/Log.h create mode 100644 lldb/include/lldb/Core/Mangled.h create mode 100644 lldb/include/lldb/Core/Module.h create mode 100644 lldb/include/lldb/Core/ModuleChild.h create mode 100644 lldb/include/lldb/Core/ModuleList.h create mode 100644 lldb/include/lldb/Core/Options.h create mode 100644 lldb/include/lldb/Core/PluginInterface.h create mode 100644 lldb/include/lldb/Core/PluginManager.h create mode 100644 lldb/include/lldb/Core/RegularExpression.h create mode 100644 lldb/include/lldb/Core/STLUtils.h create mode 100644 lldb/include/lldb/Core/Scalar.h create mode 100644 lldb/include/lldb/Core/SearchFilter.h create mode 100644 lldb/include/lldb/Core/Section.h create mode 100644 lldb/include/lldb/Core/SourceManager.h create mode 100644 lldb/include/lldb/Core/State.h create mode 100644 lldb/include/lldb/Core/Stream.h create mode 100644 lldb/include/lldb/Core/StreamFile.h create mode 100644 lldb/include/lldb/Core/StreamString.h create mode 100644 lldb/include/lldb/Core/StringList.h create mode 100644 lldb/include/lldb/Core/TTYState.h create mode 100644 lldb/include/lldb/Core/ThreadSafeSTLMap.h create mode 100644 lldb/include/lldb/Core/ThreadSafeValue.h create mode 100644 lldb/include/lldb/Core/Timer.h create mode 100644 lldb/include/lldb/Core/UUID.h create mode 100644 lldb/include/lldb/Core/UniqueCStringMap.h create mode 100644 lldb/include/lldb/Core/UserID.h create mode 100644 lldb/include/lldb/Core/VMRange.h create mode 100644 lldb/include/lldb/Core/Value.h create mode 100644 lldb/include/lldb/Core/ValueObject.h create mode 100644 lldb/include/lldb/Core/ValueObjectChild.h create mode 100644 lldb/include/lldb/Core/ValueObjectList.h create mode 100644 lldb/include/lldb/Core/ValueObjectRegister.h create mode 100644 lldb/include/lldb/Core/ValueObjectVariable.h create mode 100644 lldb/include/lldb/Core/dwarf.h create mode 100644 lldb/include/lldb/Expression/ClangASTSource.h create mode 100644 lldb/include/lldb/Expression/ClangExpression.h create mode 100644 lldb/include/lldb/Expression/ClangExpressionDeclMap.h create mode 100644 lldb/include/lldb/Expression/ClangExpressionVariable.h create mode 100644 lldb/include/lldb/Expression/ClangFunction.h create mode 100644 lldb/include/lldb/Expression/ClangStmtVisitor.h create mode 100644 lldb/include/lldb/Expression/DWARFExpression.h create mode 100644 lldb/include/lldb/Expression/RecordingMemoryManager.h create mode 100644 lldb/include/lldb/Host/Condition.h create mode 100644 lldb/include/lldb/Host/Endian.h create mode 100644 lldb/include/lldb/Host/Host.h create mode 100644 lldb/include/lldb/Host/Mutex.h create mode 100644 lldb/include/lldb/Host/Predicate.h create mode 100644 lldb/include/lldb/Host/Symbols.h create mode 100644 lldb/include/lldb/Host/TimeValue.h create mode 100644 lldb/include/lldb/Host/Types.h create mode 100644 lldb/include/lldb/Interpreter/CommandCompletions.h create mode 100644 lldb/include/lldb/Interpreter/CommandContext.h create mode 100644 lldb/include/lldb/Interpreter/CommandInterpreter.h create mode 100644 lldb/include/lldb/Interpreter/CommandObject.h create mode 100644 lldb/include/lldb/Interpreter/CommandObjectCrossref.h create mode 100644 lldb/include/lldb/Interpreter/CommandObjectMultiword.h create mode 100644 lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h create mode 100644 lldb/include/lldb/Interpreter/CommandReturnObject.h create mode 100644 lldb/include/lldb/Interpreter/ScriptInterpreter.h create mode 100644 lldb/include/lldb/Interpreter/ScriptInterpreterNone.h create mode 100644 lldb/include/lldb/Interpreter/ScriptInterpreterPython.h create mode 100644 lldb/include/lldb/Interpreter/StateVariable.h create mode 100644 lldb/include/lldb/Symbol/Block.h create mode 100644 lldb/include/lldb/Symbol/ClangASTContext.h create mode 100644 lldb/include/lldb/Symbol/CompileUnit.h create mode 100644 lldb/include/lldb/Symbol/DWARFCallFrameInfo.h create mode 100644 lldb/include/lldb/Symbol/Declaration.h create mode 100644 lldb/include/lldb/Symbol/Function.h create mode 100644 lldb/include/lldb/Symbol/LineEntry.h create mode 100644 lldb/include/lldb/Symbol/LineTable.h create mode 100644 lldb/include/lldb/Symbol/ObjectContainer.h create mode 100644 lldb/include/lldb/Symbol/ObjectFile.h create mode 100644 lldb/include/lldb/Symbol/Symbol.h create mode 100644 lldb/include/lldb/Symbol/SymbolContext.h create mode 100644 lldb/include/lldb/Symbol/SymbolContextScope.h create mode 100644 lldb/include/lldb/Symbol/SymbolFile.h create mode 100644 lldb/include/lldb/Symbol/SymbolVendor.h create mode 100644 lldb/include/lldb/Symbol/Symtab.h create mode 100644 lldb/include/lldb/Symbol/Type.h create mode 100644 lldb/include/lldb/Symbol/TypeList.h create mode 100644 lldb/include/lldb/Symbol/Variable.h create mode 100644 lldb/include/lldb/Symbol/VariableList.h create mode 100644 lldb/include/lldb/Target/ABI.h create mode 100644 lldb/include/lldb/Target/DynamicLoader.h create mode 100644 lldb/include/lldb/Target/ExecutionContext.h create mode 100644 lldb/include/lldb/Target/ExecutionContextScope.h create mode 100644 lldb/include/lldb/Target/ObjCObjectPrinter.h create mode 100644 lldb/include/lldb/Target/PathMappingList.h create mode 100644 lldb/include/lldb/Target/Process.h create mode 100644 lldb/include/lldb/Target/RegisterContext.h create mode 100644 lldb/include/lldb/Target/StackFrame.h create mode 100644 lldb/include/lldb/Target/StackFrameList.h create mode 100644 lldb/include/lldb/Target/StackID.h create mode 100644 lldb/include/lldb/Target/Target.h create mode 100644 lldb/include/lldb/Target/TargetList.h create mode 100644 lldb/include/lldb/Target/Thread.h create mode 100644 lldb/include/lldb/Target/ThreadList.h create mode 100644 lldb/include/lldb/Target/ThreadPlan.h create mode 100644 lldb/include/lldb/Target/ThreadPlanBase.h create mode 100644 lldb/include/lldb/Target/ThreadPlanCallFunction.h create mode 100644 lldb/include/lldb/Target/ThreadPlanContinue.h create mode 100644 lldb/include/lldb/Target/ThreadPlanRunToAddress.h create mode 100644 lldb/include/lldb/Target/ThreadPlanShouldStopHere.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepInRange.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepInstruction.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepOut.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepOverRange.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepRange.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepThrough.h create mode 100644 lldb/include/lldb/Target/ThreadPlanStepUntil.h create mode 100644 lldb/include/lldb/Target/UnixSignals.h create mode 100644 lldb/include/lldb/Target/Unwind.h create mode 100644 lldb/include/lldb/lldb-defines.h create mode 100644 lldb/include/lldb/lldb-enumerations.h create mode 100644 lldb/include/lldb/lldb-forward-rtti.h create mode 100644 lldb/include/lldb/lldb-forward.h create mode 100644 lldb/include/lldb/lldb-include.h create mode 100644 lldb/include/lldb/lldb-private-interfaces.h create mode 100644 lldb/include/lldb/lldb-private-log.h create mode 100644 lldb/include/lldb/lldb-private.h create mode 100644 lldb/include/lldb/lldb-types.h create mode 100644 lldb/lldb.runcontext create mode 100644 lldb/lldb.xcodeproj/project.pbxproj create mode 100644 lldb/lldb.xcworkspace/contents.xcworkspacedata create mode 100644 lldb/resources/LLDB-Info.plist create mode 100644 lldb/resources/lldb-framework-exports create mode 100755 lldb/scripts/Python/build-swig-Python.sh create mode 100644 lldb/scripts/Python/edit-swig-python-wrapper-file.py create mode 100755 lldb/scripts/Python/finish-swig-Python-LLDB.sh create mode 100644 lldb/scripts/build-llvm.pl create mode 100755 lldb/scripts/build-swig-wrapper-classes.sh create mode 100755 lldb/scripts/checkpoint-llvm.pl create mode 100755 lldb/scripts/finish-swig-wrapper-classes.sh create mode 100755 lldb/scripts/install-lldb.sh create mode 100644 lldb/scripts/lldb.swig create mode 100755 lldb/scripts/sed-sources create mode 100644 lldb/source/API/SBAddress.cpp create mode 100644 lldb/source/API/SBBlock.cpp create mode 100644 lldb/source/API/SBBreakpoint.cpp create mode 100644 lldb/source/API/SBBreakpointLocation.cpp create mode 100644 lldb/source/API/SBBroadcaster.cpp create mode 100644 lldb/source/API/SBCommandContext.cpp create mode 100644 lldb/source/API/SBCommandInterpreter.cpp create mode 100644 lldb/source/API/SBCommandReturnObject.cpp create mode 100644 lldb/source/API/SBCommunication.cpp create mode 100644 lldb/source/API/SBCompileUnit.cpp create mode 100644 lldb/source/API/SBDebugger.cpp create mode 100644 lldb/source/API/SBError.cpp create mode 100644 lldb/source/API/SBEvent.cpp create mode 100644 lldb/source/API/SBFileSpec.cpp create mode 100644 lldb/source/API/SBFrame.cpp create mode 100644 lldb/source/API/SBFunction.cpp create mode 100644 lldb/source/API/SBHostOS.cpp create mode 100644 lldb/source/API/SBInputReader.cpp create mode 100644 lldb/source/API/SBInstruction.cpp create mode 100644 lldb/source/API/SBInstructionList.cpp create mode 100644 lldb/source/API/SBLineEntry.cpp create mode 100644 lldb/source/API/SBListener.cpp create mode 100644 lldb/source/API/SBModule.cpp create mode 100644 lldb/source/API/SBProcess.cpp create mode 100644 lldb/source/API/SBSourceManager.cpp create mode 100644 lldb/source/API/SBStringList.cpp create mode 100644 lldb/source/API/SBSymbol.cpp create mode 100644 lldb/source/API/SBSymbolContext.cpp create mode 100644 lldb/source/API/SBTarget.cpp create mode 100644 lldb/source/API/SBThread.cpp create mode 100644 lldb/source/API/SBType.cpp create mode 100644 lldb/source/API/SBValue.cpp create mode 100644 lldb/source/API/SBValueList.cpp create mode 100644 lldb/source/Breakpoint/Breakpoint.cpp create mode 100644 lldb/source/Breakpoint/BreakpointID.cpp create mode 100644 lldb/source/Breakpoint/BreakpointIDList.cpp create mode 100644 lldb/source/Breakpoint/BreakpointList.cpp create mode 100644 lldb/source/Breakpoint/BreakpointLocation.cpp create mode 100644 lldb/source/Breakpoint/BreakpointLocationCollection.cpp create mode 100644 lldb/source/Breakpoint/BreakpointLocationList.cpp create mode 100644 lldb/source/Breakpoint/BreakpointOptions.cpp create mode 100644 lldb/source/Breakpoint/BreakpointResolver.cpp create mode 100644 lldb/source/Breakpoint/BreakpointResolverAddress.cpp create mode 100644 lldb/source/Breakpoint/BreakpointResolverFileLine.cpp create mode 100644 lldb/source/Breakpoint/BreakpointResolverName.cpp create mode 100644 lldb/source/Breakpoint/BreakpointSite.cpp create mode 100644 lldb/source/Breakpoint/BreakpointSiteList.cpp create mode 100644 lldb/source/Breakpoint/Stoppoint.cpp create mode 100644 lldb/source/Breakpoint/StoppointCallbackContext.cpp create mode 100644 lldb/source/Breakpoint/StoppointLocation.cpp create mode 100644 lldb/source/Breakpoint/WatchpointLocation.cpp create mode 100644 lldb/source/Commands/CommandObjectAdd.cpp create mode 100644 lldb/source/Commands/CommandObjectAdd.h create mode 100644 lldb/source/Commands/CommandObjectAlias.cpp create mode 100644 lldb/source/Commands/CommandObjectAlias.h create mode 100644 lldb/source/Commands/CommandObjectAppend.cpp create mode 100644 lldb/source/Commands/CommandObjectAppend.h create mode 100644 lldb/source/Commands/CommandObjectApropos.cpp create mode 100644 lldb/source/Commands/CommandObjectApropos.h create mode 100644 lldb/source/Commands/CommandObjectArgs.cpp create mode 100644 lldb/source/Commands/CommandObjectArgs.h create mode 100644 lldb/source/Commands/CommandObjectBreakpoint.cpp create mode 100644 lldb/source/Commands/CommandObjectBreakpoint.h create mode 100644 lldb/source/Commands/CommandObjectBreakpointCommand.cpp create mode 100644 lldb/source/Commands/CommandObjectBreakpointCommand.h create mode 100644 lldb/source/Commands/CommandObjectCall.cpp create mode 100644 lldb/source/Commands/CommandObjectCall.h create mode 100644 lldb/source/Commands/CommandObjectDelete.cpp create mode 100644 lldb/source/Commands/CommandObjectDelete.h create mode 100644 lldb/source/Commands/CommandObjectDisassemble.cpp create mode 100644 lldb/source/Commands/CommandObjectDisassemble.h create mode 100644 lldb/source/Commands/CommandObjectExpression.cpp create mode 100644 lldb/source/Commands/CommandObjectExpression.h create mode 100644 lldb/source/Commands/CommandObjectFile.cpp create mode 100644 lldb/source/Commands/CommandObjectFile.h create mode 100644 lldb/source/Commands/CommandObjectFrame.cpp create mode 100644 lldb/source/Commands/CommandObjectFrame.h create mode 100644 lldb/source/Commands/CommandObjectHelp.cpp create mode 100644 lldb/source/Commands/CommandObjectHelp.h create mode 100644 lldb/source/Commands/CommandObjectImage.cpp create mode 100644 lldb/source/Commands/CommandObjectImage.h create mode 100644 lldb/source/Commands/CommandObjectInfo.cpp create mode 100644 lldb/source/Commands/CommandObjectInfo.h create mode 100644 lldb/source/Commands/CommandObjectLog.cpp create mode 100644 lldb/source/Commands/CommandObjectLog.h create mode 100644 lldb/source/Commands/CommandObjectMemory.cpp create mode 100644 lldb/source/Commands/CommandObjectMemory.h create mode 100644 lldb/source/Commands/CommandObjectProcess.cpp create mode 100644 lldb/source/Commands/CommandObjectProcess.h create mode 100644 lldb/source/Commands/CommandObjectQuit.cpp create mode 100644 lldb/source/Commands/CommandObjectQuit.h create mode 100644 lldb/source/Commands/CommandObjectRegister.cpp create mode 100644 lldb/source/Commands/CommandObjectRegister.h create mode 100644 lldb/source/Commands/CommandObjectRemove.cpp create mode 100644 lldb/source/Commands/CommandObjectRemove.h create mode 100644 lldb/source/Commands/CommandObjectScript.cpp create mode 100644 lldb/source/Commands/CommandObjectScript.h create mode 100644 lldb/source/Commands/CommandObjectSelect.cpp create mode 100644 lldb/source/Commands/CommandObjectSelect.h create mode 100644 lldb/source/Commands/CommandObjectSet.cpp create mode 100644 lldb/source/Commands/CommandObjectSet.h create mode 100644 lldb/source/Commands/CommandObjectSettings.cpp create mode 100644 lldb/source/Commands/CommandObjectSettings.h create mode 100644 lldb/source/Commands/CommandObjectShow.cpp create mode 100644 lldb/source/Commands/CommandObjectShow.h create mode 100644 lldb/source/Commands/CommandObjectSource.cpp create mode 100644 lldb/source/Commands/CommandObjectSource.h create mode 100644 lldb/source/Commands/CommandObjectSourceFile.cpp create mode 100644 lldb/source/Commands/CommandObjectSourceFile.h create mode 100644 lldb/source/Commands/CommandObjectStatus.cpp create mode 100644 lldb/source/Commands/CommandObjectStatus.h create mode 100644 lldb/source/Commands/CommandObjectSyntax.cpp create mode 100644 lldb/source/Commands/CommandObjectSyntax.h create mode 100644 lldb/source/Commands/CommandObjectTarget.cpp create mode 100644 lldb/source/Commands/CommandObjectTarget.h create mode 100644 lldb/source/Commands/CommandObjectThread.cpp create mode 100644 lldb/source/Commands/CommandObjectThread.h create mode 100644 lldb/source/Commands/CommandObjectTranslate.cpp create mode 100644 lldb/source/Commands/CommandObjectTranslate.h create mode 100644 lldb/source/Commands/CommandObjectUnalias.cpp create mode 100644 lldb/source/Commands/CommandObjectUnalias.h create mode 100644 lldb/source/Commands/CommandObjectVariable.cpp create mode 100644 lldb/source/Commands/CommandObjectVariable.h create mode 100644 lldb/source/Core/Address.cpp create mode 100644 lldb/source/Core/AddressRange.cpp create mode 100644 lldb/source/Core/AddressResolver.cpp create mode 100644 lldb/source/Core/AddressResolverFileLine.cpp create mode 100644 lldb/source/Core/AddressResolverName.cpp create mode 100644 lldb/source/Core/ArchSpec.cpp create mode 100644 lldb/source/Core/Args.cpp create mode 100644 lldb/source/Core/Baton.cpp create mode 100644 lldb/source/Core/Broadcaster.cpp create mode 100644 lldb/source/Core/Communication.cpp create mode 100644 lldb/source/Core/Connection.cpp create mode 100644 lldb/source/Core/ConnectionFileDescriptor.cpp create mode 100644 lldb/source/Core/ConstString.cpp create mode 100644 lldb/source/Core/DataBufferHeap.cpp create mode 100644 lldb/source/Core/DataBufferMemoryMap.cpp create mode 100644 lldb/source/Core/DataExtractor.cpp create mode 100644 lldb/source/Core/Debugger.cpp create mode 100644 lldb/source/Core/Disassembler.cpp create mode 100644 lldb/source/Core/DynamicLoader.cpp create mode 100644 lldb/source/Core/Error.cpp create mode 100644 lldb/source/Core/Event.cpp create mode 100644 lldb/source/Core/FileSpec.cpp create mode 100644 lldb/source/Core/FileSpecList.cpp create mode 100644 lldb/source/Core/Flags.cpp create mode 100644 lldb/source/Core/InputReader.cpp create mode 100644 lldb/source/Core/Language.cpp create mode 100644 lldb/source/Core/Listener.cpp create mode 100644 lldb/source/Core/Log.cpp create mode 100644 lldb/source/Core/Mangled.cpp create mode 100644 lldb/source/Core/Module.cpp create mode 100644 lldb/source/Core/ModuleChild.cpp create mode 100644 lldb/source/Core/ModuleList.cpp create mode 100644 lldb/source/Core/Options.cpp create mode 100644 lldb/source/Core/PluginManager.cpp create mode 100644 lldb/source/Core/RegularExpression.cpp create mode 100644 lldb/source/Core/Scalar.cpp create mode 100644 lldb/source/Core/SearchFilter.cpp create mode 100644 lldb/source/Core/Section.cpp create mode 100644 lldb/source/Core/SourceManager.cpp create mode 100644 lldb/source/Core/State.cpp create mode 100644 lldb/source/Core/Stream.cpp create mode 100644 lldb/source/Core/StreamFile.cpp create mode 100644 lldb/source/Core/StreamString.cpp create mode 100644 lldb/source/Core/StringList.cpp create mode 100644 lldb/source/Core/TTYState.cpp create mode 100644 lldb/source/Core/Timer.cpp create mode 100644 lldb/source/Core/UUID.cpp create mode 100644 lldb/source/Core/UserID.cpp create mode 100644 lldb/source/Core/VMRange.cpp create mode 100644 lldb/source/Core/Value.cpp create mode 100644 lldb/source/Core/ValueObject.cpp create mode 100644 lldb/source/Core/ValueObjectChild.cpp create mode 100644 lldb/source/Core/ValueObjectList.cpp create mode 100644 lldb/source/Core/ValueObjectRegister.cpp create mode 100644 lldb/source/Core/ValueObjectVariable.cpp create mode 100644 lldb/source/Expression/ClangASTSource.cpp create mode 100644 lldb/source/Expression/ClangExpression.cpp create mode 100644 lldb/source/Expression/ClangExpressionDeclMap.cpp create mode 100644 lldb/source/Expression/ClangExpressionVariable.cpp create mode 100644 lldb/source/Expression/ClangFunction.cpp create mode 100644 lldb/source/Expression/ClangStmtVisitor.cpp create mode 100644 lldb/source/Expression/DWARFExpression.cpp create mode 100644 lldb/source/Expression/RecordingMemoryManager.cpp create mode 100644 lldb/source/Host/macosx/Condition.cpp create mode 100644 lldb/source/Host/macosx/Host.mm create mode 100644 lldb/source/Host/macosx/Mutex.cpp create mode 100644 lldb/source/Host/macosx/Symbols.cpp create mode 100644 lldb/source/Host/macosx/TimeValue.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCBundle.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCBundle.h create mode 100644 lldb/source/Host/macosx/cfcpp/CFCData.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCData.h create mode 100644 lldb/source/Host/macosx/cfcpp/CFCMutableArray.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCMutableArray.h create mode 100644 lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.h create mode 100644 lldb/source/Host/macosx/cfcpp/CFCMutableSet.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCMutableSet.h create mode 100644 lldb/source/Host/macosx/cfcpp/CFCReleaser.h create mode 100644 lldb/source/Host/macosx/cfcpp/CFCString.cpp create mode 100644 lldb/source/Host/macosx/cfcpp/CFCString.h create mode 100644 lldb/source/Host/macosx/cfcpp/CoreFoundationCPP.h create mode 100644 lldb/source/Interpreter/CommandCompletions.cpp create mode 100644 lldb/source/Interpreter/CommandContext.cpp create mode 100644 lldb/source/Interpreter/CommandInterpreter.cpp create mode 100644 lldb/source/Interpreter/CommandObject.cpp create mode 100644 lldb/source/Interpreter/CommandObjectCrossref.cpp create mode 100644 lldb/source/Interpreter/CommandObjectMultiword.cpp create mode 100644 lldb/source/Interpreter/CommandObjectRegexCommand.cpp create mode 100644 lldb/source/Interpreter/CommandReturnObject.cpp create mode 100644 lldb/source/Interpreter/ScriptInterpreter.cpp create mode 100644 lldb/source/Interpreter/ScriptInterpreterNone.cpp create mode 100644 lldb/source/Interpreter/ScriptInterpreterPython.cpp create mode 100644 lldb/source/Interpreter/StateVariable.cpp create mode 100644 lldb/source/Interpreter/embedded_interpreter.py create mode 100644 lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp create mode 100644 lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h create mode 100644 lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp create mode 100644 lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h create mode 100644 lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp create mode 100644 lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h create mode 100644 lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp create mode 100644 lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h create mode 100644 lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp create mode 100644 lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h create mode 100644 lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp create mode 100644 lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h create mode 100644 lldb/source/Plugins/ObjectFile/ELF/elf.h create mode 100644 lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp create mode 100644 lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig create mode 100644 lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl create mode 100755 lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp create mode 100644 lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h create mode 100644 lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp create mode 100644 lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h create mode 100644 lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp create mode 100644 lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h create mode 100644 lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp create mode 100644 lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h create mode 100644 lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp create mode 100644 lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h create mode 100644 lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp create mode 100644 lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx create mode 100644 lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h create mode 100644 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h create mode 100644 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h create mode 100644 lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp create mode 100644 lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp create mode 100644 lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h create mode 100644 lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp create mode 100644 lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h create mode 100644 lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp create mode 100644 lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h create mode 100644 lldb/source/Symbol/Block.cpp create mode 100644 lldb/source/Symbol/ClangASTContext.cpp create mode 100644 lldb/source/Symbol/CompileUnit.cpp create mode 100644 lldb/source/Symbol/DWARFCallFrameInfo.cpp create mode 100644 lldb/source/Symbol/Declaration.cpp create mode 100644 lldb/source/Symbol/Function.cpp create mode 100644 lldb/source/Symbol/LineEntry.cpp create mode 100644 lldb/source/Symbol/LineTable.cpp create mode 100644 lldb/source/Symbol/ObjectFile.cpp create mode 100644 lldb/source/Symbol/Symbol.cpp create mode 100644 lldb/source/Symbol/SymbolContext.cpp create mode 100644 lldb/source/Symbol/SymbolFile.cpp create mode 100644 lldb/source/Symbol/SymbolVendor.mm create mode 100644 lldb/source/Symbol/Symtab.cpp create mode 100644 lldb/source/Symbol/Type.cpp create mode 100644 lldb/source/Symbol/TypeList.cpp create mode 100644 lldb/source/Symbol/Variable.cpp create mode 100644 lldb/source/Symbol/VariableList.cpp create mode 100644 lldb/source/Target/ABI.cpp create mode 100644 lldb/source/Target/ExecutionContext.cpp create mode 100644 lldb/source/Target/ObjCObjectPrinter.cpp create mode 100644 lldb/source/Target/PathMappingList.cpp create mode 100644 lldb/source/Target/Process.cpp create mode 100644 lldb/source/Target/RegisterContext.cpp create mode 100644 lldb/source/Target/StackFrame.cpp create mode 100644 lldb/source/Target/StackFrameList.cpp create mode 100644 lldb/source/Target/StackID.cpp create mode 100644 lldb/source/Target/Target.cpp create mode 100644 lldb/source/Target/TargetList.cpp create mode 100644 lldb/source/Target/Thread.cpp create mode 100644 lldb/source/Target/ThreadList.cpp create mode 100644 lldb/source/Target/ThreadPlan.cpp create mode 100644 lldb/source/Target/ThreadPlanBase.cpp create mode 100644 lldb/source/Target/ThreadPlanCallFunction.cpp create mode 100644 lldb/source/Target/ThreadPlanContinue.cpp create mode 100644 lldb/source/Target/ThreadPlanRunToAddress.cpp create mode 100644 lldb/source/Target/ThreadPlanShouldStopHere.cpp create mode 100644 lldb/source/Target/ThreadPlanStepInRange.cpp create mode 100644 lldb/source/Target/ThreadPlanStepInstruction.cpp create mode 100644 lldb/source/Target/ThreadPlanStepOut.cpp create mode 100644 lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp create mode 100644 lldb/source/Target/ThreadPlanStepOverRange.cpp create mode 100644 lldb/source/Target/ThreadPlanStepRange.cpp create mode 100644 lldb/source/Target/ThreadPlanStepThrough.cpp create mode 100644 lldb/source/Target/ThreadPlanStepUntil.cpp create mode 100644 lldb/source/Target/UnixSignals.cpp create mode 100644 lldb/source/Utility/ARM_DWARF_Registers.h create mode 100644 lldb/source/Utility/ARM_GCC_Registers.h create mode 100644 lldb/source/Utility/PseudoTerminal.cpp create mode 100644 lldb/source/Utility/PseudoTerminal.h create mode 100644 lldb/source/Utility/StringExtractor.cpp create mode 100644 lldb/source/Utility/StringExtractor.h create mode 100644 lldb/source/Utility/StringExtractorGDBRemote.cpp create mode 100644 lldb/source/Utility/StringExtractorGDBRemote.h create mode 100644 lldb/source/lldb-log.cpp create mode 100644 lldb/source/lldb.cpp create mode 100644 lldb/test/Makefile create mode 100644 lldb/test/array_types/Makefile create mode 100644 lldb/test/array_types/cmds.txt create mode 100644 lldb/test/array_types/main.c create mode 100644 lldb/test/bitfields/Makefile create mode 100644 lldb/test/bitfields/main.c create mode 100644 lldb/test/class_types/Makefile create mode 100644 lldb/test/class_types/cmds.txt create mode 100644 lldb/test/class_types/main.cpp create mode 100644 lldb/test/dead-strip/Makefile create mode 100644 lldb/test/dead-strip/cmds.txt create mode 100644 lldb/test/dead-strip/main.c create mode 100644 lldb/test/enum_types/Makefile create mode 100644 lldb/test/enum_types/main.c create mode 100644 lldb/test/function_types/Makefile create mode 100644 lldb/test/function_types/main.c create mode 100644 lldb/test/global_variables/Makefile create mode 100644 lldb/test/global_variables/a.c create mode 100644 lldb/test/global_variables/cmds.txt create mode 100644 lldb/test/global_variables/main.c create mode 100644 lldb/test/load_unload/Makefile create mode 100644 lldb/test/load_unload/a.c create mode 100644 lldb/test/load_unload/b.c create mode 100644 lldb/test/load_unload/c.c create mode 100644 lldb/test/load_unload/cmds.txt create mode 100644 lldb/test/load_unload/main.c create mode 100644 lldb/test/namespace/Makefile create mode 100644 lldb/test/namespace/cmds.txt create mode 100644 lldb/test/namespace/main.cpp create mode 100644 lldb/test/order/Makefile create mode 100644 lldb/test/order/cmds.txt create mode 100644 lldb/test/order/main.c create mode 100644 lldb/test/order/order-file create mode 100644 lldb/test/print-obj/Makefile create mode 100644 lldb/test/print-obj/blocked.m create mode 100644 lldb/test/set_values/Makefile create mode 100644 lldb/test/set_values/main.c create mode 100644 lldb/test/signed_types/Makefile create mode 100644 lldb/test/signed_types/main.cpp create mode 100644 lldb/test/stl/Makefile create mode 100644 lldb/test/stl/cmds.txt create mode 100644 lldb/test/stl/main.cpp create mode 100644 lldb/test/struct_types/Makefile create mode 100644 lldb/test/struct_types/cmds.txt create mode 100644 lldb/test/struct_types/main.c create mode 100755 lldb/test/tester.py create mode 100644 lldb/test/threads/Makefile create mode 100644 lldb/test/threads/main.cpp create mode 100644 lldb/test/unsigned_types/Makefile create mode 100644 lldb/test/unsigned_types/main.cpp create mode 100644 lldb/tools/debugserver/debugnub-exports create mode 100644 lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj create mode 100644 lldb/tools/debugserver/resources/lldb-debugserver-Info.plist create mode 100644 lldb/tools/debugserver/resources/lldb-debugserver-entitlements.plist create mode 100644 lldb/tools/debugserver/scripts/dbgnub-config.pl create mode 100644 lldb/tools/debugserver/source/ChangeLog create mode 100644 lldb/tools/debugserver/source/DNB.cpp create mode 100644 lldb/tools/debugserver/source/DNB.h create mode 100644 lldb/tools/debugserver/source/DNBArch.h create mode 100644 lldb/tools/debugserver/source/DNBBreakpoint.cpp create mode 100644 lldb/tools/debugserver/source/DNBBreakpoint.h create mode 100644 lldb/tools/debugserver/source/DNBDataRef.cpp create mode 100644 lldb/tools/debugserver/source/DNBDataRef.h create mode 100644 lldb/tools/debugserver/source/DNBDefs.h create mode 100644 lldb/tools/debugserver/source/DNBError.cpp create mode 100644 lldb/tools/debugserver/source/DNBError.h create mode 100644 lldb/tools/debugserver/source/DNBLog.cpp create mode 100644 lldb/tools/debugserver/source/DNBLog.h create mode 100644 lldb/tools/debugserver/source/DNBRegisterInfo.cpp create mode 100644 lldb/tools/debugserver/source/DNBRegisterInfo.h create mode 100644 lldb/tools/debugserver/source/DNBRuntimeAction.h create mode 100644 lldb/tools/debugserver/source/DNBThreadResumeActions.cpp create mode 100644 lldb/tools/debugserver/source/DNBThreadResumeActions.h create mode 100644 lldb/tools/debugserver/source/DNBTimer.h create mode 100644 lldb/tools/debugserver/source/FunctionProfiler.cpp create mode 100644 lldb/tools/debugserver/source/FunctionProfiler.h create mode 100644 lldb/tools/debugserver/source/MacOSX/CFBundle.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/CFBundle.h create mode 100644 lldb/tools/debugserver/source/MacOSX/CFData.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/CFData.h create mode 100644 lldb/tools/debugserver/source/MacOSX/CFString.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/CFString.h create mode 100644 lldb/tools/debugserver/source/MacOSX/CFUtils.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachDYLD.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachException.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachException.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachProcess.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachProcess.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachTask.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachTask.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachThread.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachThread.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachThreadList.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachVMMemory.h create mode 100644 lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/MachVMRegion.h create mode 100644 lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h create mode 100644 lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs create mode 100644 lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h create mode 100644 lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h create mode 100644 lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp create mode 100644 lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h create mode 100644 lldb/tools/debugserver/source/PThreadCondition.h create mode 100644 lldb/tools/debugserver/source/PThreadEvent.cpp create mode 100644 lldb/tools/debugserver/source/PThreadEvent.h create mode 100644 lldb/tools/debugserver/source/PThreadMutex.cpp create mode 100644 lldb/tools/debugserver/source/PThreadMutex.h create mode 100644 lldb/tools/debugserver/source/ProfileObjectiveC.cpp create mode 100644 lldb/tools/debugserver/source/ProfileObjectiveC.h create mode 100644 lldb/tools/debugserver/source/PseudoTerminal.cpp create mode 100644 lldb/tools/debugserver/source/PseudoTerminal.h create mode 100644 lldb/tools/debugserver/source/RNBContext.cpp create mode 100644 lldb/tools/debugserver/source/RNBContext.h create mode 100644 lldb/tools/debugserver/source/RNBDefs.h create mode 100644 lldb/tools/debugserver/source/RNBRemote.cpp create mode 100644 lldb/tools/debugserver/source/RNBRemote.h create mode 100644 lldb/tools/debugserver/source/RNBServices.cpp create mode 100644 lldb/tools/debugserver/source/RNBServices.h create mode 100644 lldb/tools/debugserver/source/RNBSocket.cpp create mode 100644 lldb/tools/debugserver/source/RNBSocket.h create mode 100644 lldb/tools/debugserver/source/SysSignal.cpp create mode 100644 lldb/tools/debugserver/source/SysSignal.h create mode 100644 lldb/tools/debugserver/source/TTYState.cpp create mode 100644 lldb/tools/debugserver/source/TTYState.h create mode 100644 lldb/tools/debugserver/source/com.apple.debugserver.applist.plist create mode 100644 lldb/tools/debugserver/source/com.apple.debugserver.plist create mode 100644 lldb/tools/debugserver/source/debugserver-entitlements.plist create mode 100644 lldb/tools/debugserver/source/debugserver.cpp create mode 100644 lldb/tools/driver/Driver.cpp create mode 100644 lldb/tools/driver/Driver.h create mode 100644 lldb/tools/driver/IOChannel.cpp create mode 100644 lldb/tools/driver/IOChannel.h create mode 100644 lldb/tools/driver/lldb-Info.plist create mode 100644 lldb/www/content.css create mode 100644 lldb/www/index.html create mode 100644 lldb/www/menu.css create mode 100644 lldb/www/menu.html.incl diff --git a/lldb/docs/code-signing.txt b/lldb/docs/code-signing.txt new file mode 100644 index 000000000000..77cc09993243 --- /dev/null +++ b/lldb/docs/code-signing.txt @@ -0,0 +1,47 @@ +On MacOSX lldb needs to be code signed. The Debug and Release builds +are set to code sign using a code signing certificate named +lldb_codesign. + +If you don't have one yet you will need to: +- Launch /Applications/Utilities/Keychain Access.app + +- In Keychain Access select the "login" keychain in the "Keychains" + list in the upper left hand corner of the window. + +- Select the following menu item: + + Keychain Access->Certificate Assistant->Create a Certificate... + +- Set the following settings + + Name = lldb_codesign + Identity Type = Self Signed Root + Certificate Type = Code Signing + +- Click Continue +- Click Continue +- Click Done +- Click on the "My Certificates" +- Double click on your new lldb_codesign certificate +- Turn down the "Trust" disclosure triangle + + Change: + When using this certificate: Always Trust + +- Enter your login password to confirm and make it trusted + +The next steps are necessary on SnowLeopard, but are probably because of a bug +how Keychain Access makes certificates (the steps above used to be enougnk +in Leopard.) + +- Option-drag the new lldb_codesign certificate from the login keychain to + the System keychain in the Keychains pane of the main Keychain Access window + to make a copy of this certificate in the System keychain. You'll have to + authorize a few more times, set it to be "Always trusted" when asked. +- Switch to the System keychain, and drag the copy of lldb_codesign you just + made there onto the desktop. +- Switch to Terminal, and run the following: + +sudo security add-trust -d -r trustRoot -p basic -p codeSign -k /Library/Keychains/System.keychain ~/Desktop/lldb_codesign.cer + +That should do it. diff --git a/lldb/docs/code_signing.txt b/lldb/docs/code_signing.txt new file mode 100644 index 000000000000..77cc09993243 --- /dev/null +++ b/lldb/docs/code_signing.txt @@ -0,0 +1,47 @@ +On MacOSX lldb needs to be code signed. The Debug and Release builds +are set to code sign using a code signing certificate named +lldb_codesign. + +If you don't have one yet you will need to: +- Launch /Applications/Utilities/Keychain Access.app + +- In Keychain Access select the "login" keychain in the "Keychains" + list in the upper left hand corner of the window. + +- Select the following menu item: + + Keychain Access->Certificate Assistant->Create a Certificate... + +- Set the following settings + + Name = lldb_codesign + Identity Type = Self Signed Root + Certificate Type = Code Signing + +- Click Continue +- Click Continue +- Click Done +- Click on the "My Certificates" +- Double click on your new lldb_codesign certificate +- Turn down the "Trust" disclosure triangle + + Change: + When using this certificate: Always Trust + +- Enter your login password to confirm and make it trusted + +The next steps are necessary on SnowLeopard, but are probably because of a bug +how Keychain Access makes certificates (the steps above used to be enougnk +in Leopard.) + +- Option-drag the new lldb_codesign certificate from the login keychain to + the System keychain in the Keychains pane of the main Keychain Access window + to make a copy of this certificate in the System keychain. You'll have to + authorize a few more times, set it to be "Always trusted" when asked. +- Switch to the System keychain, and drag the copy of lldb_codesign you just + made there onto the desktop. +- Switch to Terminal, and run the following: + +sudo security add-trust -d -r trustRoot -p basic -p codeSign -k /Library/Keychains/System.keychain ~/Desktop/lldb_codesign.cer + +That should do it. diff --git a/lldb/include/lldb/API/LLDB.h b/lldb/include/lldb/API/LLDB.h new file mode 100644 index 000000000000..729d393f5efd --- /dev/null +++ b/lldb/include/lldb/API/LLDB.h @@ -0,0 +1,49 @@ +//===-- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // LLDB_LLDB_h_ diff --git a/lldb/include/lldb/API/SBAddress.h b/lldb/include/lldb/API/SBAddress.h new file mode 100644 index 000000000000..4f0195aa5615 --- /dev/null +++ b/lldb/include/lldb/API/SBAddress.h @@ -0,0 +1,75 @@ +//===-- 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 + +namespace lldb { + +class SBAddress +{ +public: + + SBAddress (); + + SBAddress (const lldb::SBAddress &rhs); + + ~SBAddress (); + +#ifndef SWIG + const SBAddress & + operator = (const SBAddress &rhs); +#endif + + bool + IsValid () const; + + addr_t + GetFileAddress () const; + + addr_t + GetLoadAddress (const lldb::SBProcess &process) const; + + bool + OffsetAddress (addr_t offset); + +protected: + + friend class SBFrame; + friend class SBLineEntry; + friend class SBSymbolContext; + friend class SBThread; + +#ifndef SWIG + + const lldb_private::Address * + operator->() const; + + const lldb_private::Address & + operator*() const; + +#endif + + + SBAddress (const lldb_private::Address *lldb_object_ptr); + + void + SetAddress (const lldb_private::Address *lldb_object_ptr); + +private: + + std::auto_ptr m_lldb_object_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBAddress_h_ diff --git a/lldb/include/lldb/API/SBBlock.h b/lldb/include/lldb/API/SBBlock.h new file mode 100644 index 000000000000..b8eef00d72e9 --- /dev/null +++ b/lldb/include/lldb/API/SBBlock.h @@ -0,0 +1,44 @@ +//===-- 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 + +namespace lldb { + +class SBBlock +{ +public: + + SBBlock (); + + ~SBBlock (); + + bool + IsValid () const; + + void + AppendVariables (bool can_create, bool get_parent_variables, lldb_private::VariableList *var_list); + +private: + friend class SBFrame; + friend class SBSymbolContext; + + SBBlock (lldb_private::Block *lldb_object_ptr); + + + lldb_private::Block *m_lldb_object_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBBlock_h_ diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h new file mode 100644 index 000000000000..71394749e0af --- /dev/null +++ b/lldb/include/lldb/API/SBBreakpoint.h @@ -0,0 +1,129 @@ +//===-- 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 + +namespace lldb { + +class SBBreakpoint +{ +public: + + typedef bool (*BreakpointHitCallback) (void *baton, + SBProcess &process, + SBThread &thread, + lldb::SBBreakpointLocation &location); + + SBBreakpoint (); + + SBBreakpoint (const lldb::SBBreakpoint& rhs); + + ~SBBreakpoint(); + +#ifndef SWIG + const SBBreakpoint & + operator = (const SBBreakpoint& rhs); +#endif + + break_id_t + GetID () const; + + bool + IsValid() const; + + void + Dump (FILE *f); + + 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 + ListLocations (FILE *, const char *description_level = "full"); + + void + SetEnabled (bool enable); + + bool + IsEnabled (); + + void + SetIgnoreCount (int32_t count); + + int32_t + GetIgnoreCount () const; + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetCallback (BreakpointHitCallback callback, void *baton); + + size_t + GetNumResolvedLocations() const; + + size_t + GetNumLocations() const; + + void + GetDescription (FILE *, const char *description_level, bool describe_locations = false); + + + +private: + friend class SBBreakpointLocation; + friend class SBTarget; + + SBBreakpoint (const lldb::BreakpointSP &bp_sp); + +#ifndef SWIG + + lldb_private::Breakpoint * + operator->() const; + + lldb_private::Breakpoint * + get() const; + + lldb::BreakpointSP & + operator *(); + + const lldb::BreakpointSP & + operator *() const; + +#endif + + 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_break_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBBreakpoint_h_ diff --git a/lldb/include/lldb/API/SBBreakpointLocation.h b/lldb/include/lldb/API/SBBreakpointLocation.h new file mode 100644 index 000000000000..836995676a30 --- /dev/null +++ b/lldb/include/lldb/API/SBBreakpointLocation.h @@ -0,0 +1,73 @@ +//===-- 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 +#include + +namespace lldb { + +class SBBreakpointLocation +{ +public: + + SBBreakpointLocation (); + + ~SBBreakpointLocation (); + + bool + IsValid() const; + + lldb::addr_t + GetLoadAddress (); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + int32_t + GetIgnoreCount (); + + void + SetIgnoreCount (int32_t n); + + void + SetThreadID (lldb::tid_t thread_id); + + lldb::tid_t + GetThreadID (); + + bool + IsResolved (); + + void + GetDescription (FILE *f, const char *description_level); + + SBBreakpoint + GetBreakpoint (); + +private: + friend class SBBreakpoint; + + SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp); + + void + SetLocation (const lldb::BreakpointLocationSP &break_loc_sp); + + lldb::BreakpointLocationSP m_break_loc_sp; + +}; + +} // namespace lldb + +#endif // LLDB_SBBreakpointLocation_h_ diff --git a/lldb/include/lldb/API/SBBroadcaster.h b/lldb/include/lldb/API/SBBroadcaster.h new file mode 100644 index 000000000000..94baef2f015e --- /dev/null +++ b/lldb/include/lldb/API/SBBroadcaster.h @@ -0,0 +1,83 @@ +//===-- 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 + +namespace lldb { + +class SBBroadcaster +{ +public: + SBBroadcaster (); + + SBBroadcaster (const char *name); + + ~SBBroadcaster(); + + bool + IsValid () const; + + 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 (); + + bool + EventTypeHasListeners (uint32_t event_type); + + bool + RemoveListener (const lldb::SBListener &listener, uint32_t event_mask = UINT32_MAX); + +#ifndef SWIG + bool + operator == (const lldb::SBBroadcaster &rhs) const; + + bool + operator != (const lldb::SBBroadcaster &rhs) const; + +#endif + +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 * + GetLLDBObjectPtr () const; + + void + SetLLDBObjectPtr (lldb_private::Broadcaster *broadcaster, bool owns); + +private: + + lldb_private::Broadcaster *m_lldb_object; + bool m_lldb_object_owned; +}; + +} // namespace lldb + +#endif // LLDB_SBBroadcaster_h_ diff --git a/lldb/include/lldb/API/SBCommandContext.h b/lldb/include/lldb/API/SBCommandContext.h new file mode 100644 index 000000000000..c77465cbd7a0 --- /dev/null +++ b/lldb/include/lldb/API/SBCommandContext.h @@ -0,0 +1,36 @@ +//===-- SBCommandContext.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_SBCommandContext_h_ +#define LLDB_SBCommandContext_h_ + + +#include + +namespace lldb { + +class SBCommandContext +{ +public: + + SBCommandContext (lldb_private::CommandContext *lldb_object); + + ~SBCommandContext (); + + bool + IsValid () const; + +private: + + lldb_private::CommandContext *m_lldb_object; +}; + +} // namespace lldb + +#endif // LLDB_SBCommandContext_h_ diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h new file mode 100644 index 000000000000..bb71cf59d274 --- /dev/null +++ b/lldb/include/lldb/API/SBCommandInterpreter.h @@ -0,0 +1,105 @@ +//===-- 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 + +namespace lldb { + +class SBCommandInterpreter +{ +public: + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2) // User entered quit + }; + + + ~SBCommandInterpreter (); + + bool + CommandExists (const char *cmd); + + bool + AliasExists (const char *cmd); + + bool + UserCommandExists (const char *cmd); + + lldb::SBBroadcaster + GetBroadcaster (); + + const char ** + GetEnvironmentVariables (); + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasUserCommands (); + + bool + HasAliasOptions (); + + bool + HasInterpreterVariables (); + + lldb::SBProcess + GetProcess (); + + ssize_t + WriteToScriptInterpreter (const char *src); + + ssize_t + WriteToScriptInterpreter (const char *src, size_t src_len); + + 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); + + int + HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + SBStringList &matches); + +protected: + + lldb_private::CommandInterpreter & + GetLLDBObjectRef (); + + lldb_private::CommandInterpreter * + GetLLDBObjectPtr (); + +private: + friend class SBDebugger; + + SBCommandInterpreter (lldb_private::CommandInterpreter &interpreter_ptr); // Access using SBDebugger::GetSharedInstance().GetCommandInterpreter(); + + lldb_private::CommandInterpreter &m_interpreter; +}; + + +} // namespace lldb + +#endif // LLDB_SBCommandInterpreter_h_ diff --git a/lldb/include/lldb/API/SBCommandReturnObject.h b/lldb/include/lldb/API/SBCommandReturnObject.h new file mode 100644 index 000000000000..65ddc70c1244 --- /dev/null +++ b/lldb/include/lldb/API/SBCommandReturnObject.h @@ -0,0 +1,80 @@ +//===-- 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 + +namespace lldb { + +class SBCommandReturnObject +{ +public: + + SBCommandReturnObject (); + + ~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(); + + bool + Succeeded (); + + bool + HasResult (); + + void + AppendMessage (const char *message); + +protected: + friend class SBCommandInterpreter; + friend class SBOptions; + + lldb_private::CommandReturnObject * + GetLLDBObjectPtr(); + + lldb_private::CommandReturnObject & + GetLLDBObjectRef(); + + void + SetLLDBObjectPtr (lldb_private::CommandReturnObject *ptr); + + private: + std::auto_ptr m_return_object_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBCommandReturnObject_h_ diff --git a/lldb/include/lldb/API/SBCommunication.h b/lldb/include/lldb/API/SBCommunication.h new file mode 100644 index 000000000000..134a78a557d2 --- /dev/null +++ b/lldb/include/lldb/API/SBCommunication.h @@ -0,0 +1,97 @@ +//===-- 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 +#include + +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 (); + + + lldb::SBBroadcaster + GetBroadcaster (); + + lldb::ConnectionStatus + AdoptFileDesriptor (int fd, bool owns_fd); + + lldb::ConnectionStatus + CheckIfBytesAvailable (); + + lldb::ConnectionStatus + WaitForBytesAvailableInfinite (); + + lldb::ConnectionStatus + WaitForBytesAvailableWithTimeout (uint32_t timeout_usec); + + lldb::ConnectionStatus + Connect (const char *url); + + lldb::ConnectionStatus + Disconnect (); + + bool + IsConnected () const; + + 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: +// void +// CreateIfNeeded (); + + lldb_private::Communication *m_lldb_object; + bool m_lldb_object_owned; +}; + + +} // namespace lldb + +#endif // LLDB_SBCommunication_h_ diff --git a/lldb/include/lldb/API/SBCompileUnit.h b/lldb/include/lldb/API/SBCompileUnit.h new file mode 100644 index 000000000000..3d6e7e48b91f --- /dev/null +++ b/lldb/include/lldb/API/SBCompileUnit.h @@ -0,0 +1,75 @@ +//===-- 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 +#include + +namespace lldb { + +class SBCompileUnit +{ +public: + + SBCompileUnit (); + + ~SBCompileUnit (); + + 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; + +#ifndef SWIG + + bool + operator == (const lldb::SBCompileUnit &rhs) const; + + bool + operator != (const lldb::SBCompileUnit &rhs) const; + +#endif + +private: + friend class SBFrame; + friend class SBSymbolContext; + + SBCompileUnit (lldb_private::CompileUnit *lldb_object_ptr); + +#ifndef SWIG + + const lldb_private::CompileUnit * + operator->() const; + + const lldb_private::CompileUnit & + operator*() const; + +#endif + + lldb_private::CompileUnit *m_lldb_object_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBCompileUnit_h_ diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h new file mode 100644 index 000000000000..3dcc4890f5f6 --- /dev/null +++ b/lldb/include/lldb/API/SBDebugger.h @@ -0,0 +1,148 @@ +//===-- 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 + +namespace lldb { + +class SBDebugger +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + static void + SetAsync (bool b); + + static void + SetInputFile (const char *tty_name); // DEPRECATED: will be removed in next submission + + static void + SetOutputFile (const char *tty_name); // DEPRECATED: will be removed in next submission + + static void + SetErrorFile (const char *tty_name); // DEPRECATED: will be removed in next submission + + static void + SetInputFileHandle (FILE *f, bool transfer_ownership); + + static void + SetOutputFileHandle (FILE *f, bool transfer_ownership); + + static void + SetErrorFileHandle (FILE *f, bool transfer_ownership); + + static FILE * + GetInputFileHandle (); + + static FILE * + GetOutputFileHandle (); + + static FILE * + GetErrorFileHandle (); + + static lldb::SBCommandInterpreter + GetCommandInterpreter (); + + static void + HandleCommand (const char *command); + + static lldb::SBListener + GetListener (); + + static void + HandleProcessEvent (const lldb::SBProcess &process, + const lldb::SBEvent &event, + FILE *out, + FILE *err); + + static lldb::SBTarget + CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple); + + static lldb::SBTarget + CreateTargetWithFileAndArch (const char *filename, + const char *archname); + + static lldb::SBTarget + CreateTarget (const char *filename); + + static lldb::SBTarget + GetTargetAtIndex (uint32_t idx); + + static lldb::SBTarget + FindTargetWithProcessID (pid_t pid); + + static lldb::SBTarget + FindTargetWithFileAndArch (const char *filename, + const char *arch); + + static uint32_t + GetNumTargets (); + + static lldb::SBTarget + GetCurrentTarget (); + + static void + UpdateCurrentThread (lldb::SBProcess &process); + + static void + ReportCurrentLocation (FILE *out = stdout, + FILE *err = stderr); + + static lldb::SBSourceManager & + GetSourceManager (); + + static bool + GetDefaultArchitecture (char *arch_name, size_t arch_name_len); + + static bool + SetDefaultArchitecture (const char *arch_name); + + static 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); + + static void + DispatchInput (void *baton, const void *data, size_t data_len); + + static void + PushInputReader (lldb::SBInputReader &reader); + +private: +#ifndef SWIG + friend class SBProcess; + + static lldb::SBTarget + FindTargetWithLLDBProcess (const lldb::ProcessSP &processSP); +#endif +}; // class SBDebugger + + +} // namespace lldb + +#endif // LLDB_SBDebugger_h_ diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h new file mode 100644 index 000000000000..f1e3c38a42e2 --- /dev/null +++ b/lldb/include/lldb/API/SBDefines.h @@ -0,0 +1,62 @@ +//===-- 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 +#include +#include +#include +#include + +// Forward Declarations + +namespace lldb { + +class SBAddress; +class SBBlock; +class SBBreakpoint; +class SBBreakpointLocation; +class SBBroadcaster; +class SBCommandContext; +class SBCommandInterpreter; +class SBCommandReturnObject; +class SBCommunication; +class SBCompileUnit; +class SBDebugger; +class SBError; +class SBEvent; +class SBEventList; +class SBFileSpec; +class SBFrame; +class SBFunction; +class SBHostOS; +class SBInputReader; +class SBInstruction; +class SBLineEntry; +class SBListener; +class SBModule; +class SBProcess; +class SBSourceManager; +class SBSymbol; +class SBSymbolContext; +class SBStringList; +class SBTarget; +class SBThread; +class SBValue; + +} + +#endif // LLDB_SBDefines_h_ diff --git a/lldb/include/lldb/API/SBError.h b/lldb/include/lldb/API/SBError.h new file mode 100644 index 000000000000..1d23c736d2a7 --- /dev/null +++ b/lldb/include/lldb/API/SBError.h @@ -0,0 +1,102 @@ +//===-- 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 + +namespace lldb { + +class SBError { +public: + SBError (); + + SBError (const SBError &rhs); + + ~SBError(); + +#ifndef SWIG + + const SBError & + operator =(const SBError &rhs); + +#endif + + 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, ...); + + bool + IsValid () const; + +protected: + friend class SBArguments; + friend class SBCommunication; + friend class SBHostOS; + friend class SBInputReader; + friend class SBProcess; + +#ifndef SWIG + + lldb_private::Error * + get(); + + lldb_private::Error * + operator->(); + + const lldb_private::Error & + operator*() const; + +#endif + + + void + SetError (const lldb_private::Error &lldb_error); + +private: + std::auto_ptr m_lldb_object_ap; + + void + CreateIfNeeded (); +}; + + +} // namespace lldb + +#endif // LLDB_SBError_h_ diff --git a/lldb/include/lldb/API/SBEvent.h b/lldb/include/lldb/API/SBEvent.h new file mode 100644 index 000000000000..2267d8d4ce2e --- /dev/null +++ b/lldb/include/lldb/API/SBEvent.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_SBEvent_h_ +#define LLDB_SBEvent_h_ + +#include + +#include + +namespace lldb { + +class SBBroadcaster; + +class SBEvent +{ +public: + SBEvent(); + + // Make an event that contains a C string. + SBEvent (uint32_t event, const char *cstr, uint32_t cstr_len); + + ~SBEvent(); + + bool + IsValid() const; + + void + Dump (FILE *f) const; + + const char * + GetDataFlavor (); + + uint32_t + GetType () const; + + lldb::SBBroadcaster + GetBroadcaster () const; + + bool + BroadcasterMatchesPtr (const lldb::SBBroadcaster *broadcaster); + + bool + BroadcasterMatchesRef (const lldb::SBBroadcaster &broadcaster); + + void + Clear(); + + static const char * + GetCStringFromEvent (const lldb::SBEvent &event); + +protected: + friend class SBListener; + friend class SBBroadcaster; + friend class SBDebugger; + friend class SBProcess; + + SBEvent (lldb::EventSP &event_sp); + + lldb::EventSP & + GetSharedPtr () const; + + void + SetEventSP (lldb::EventSP &event_sp); + + void + SetLLDBObjectPtr (lldb_private::Event* event); + + lldb_private::Event * + GetLLDBObjectPtr (); + + const lldb_private::Event * + GetLLDBObjectPtr () const; + +private: + + mutable lldb::EventSP m_event_sp; + mutable lldb_private::Event *m_lldb_object; +}; + +} // namespace lldb + +#endif // LLDB_SBEvent_h_ diff --git a/lldb/include/lldb/API/SBFileSpec.h b/lldb/include/lldb/API/SBFileSpec.h new file mode 100644 index 000000000000..dd6718ec9426 --- /dev/null +++ b/lldb/include/lldb/API/SBFileSpec.h @@ -0,0 +1,83 @@ +//===-- 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 + +namespace lldb { + +class SBFileSpec +{ +public: + SBFileSpec (); + + SBFileSpec (const lldb::SBFileSpec &rhs); + + SBFileSpec (const char *path); + + ~SBFileSpec (); + +#ifndef SWIG + const SBFileSpec & + operator = (const lldb::SBFileSpec &rhs); +#endif + + bool + IsValid() const; + + bool + Exists () const; + + 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); + +private: + friend class SBLineEntry; + friend class SBCompileUnit; + friend class SBHostOS; + friend class SBModule; + friend class SBSourceManager; + friend class SBTarget; + + void + SetFileSpec (const lldb_private::FileSpec& fs); +#ifndef SWIG + + const lldb_private::FileSpec * + operator->() const; + + const lldb_private::FileSpec * + get() const; + + const lldb_private::FileSpec & + operator*() const; + + const lldb_private::FileSpec & + ref() const; + +#endif + + std::auto_ptr m_lldb_object_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBFileSpec_h_ diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h new file mode 100644 index 000000000000..36106bdbff5d --- /dev/null +++ b/lldb/include/lldb/API/SBFrame.h @@ -0,0 +1,130 @@ +//===-- 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 +#include + +namespace lldb { + +class SBValue; + +class SBFrame +{ +public: + SBFrame (); + + ~SBFrame(); + + 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::SBBlock + GetBlock () const; + + lldb::SBLineEntry + GetLineEntry () const; + + lldb::SBThread + GetThread () const; + + const char * + Disassemble () const; + + void + Clear(); + +#ifndef SWIG + bool + operator == (const lldb::SBFrame &rhs) const; + + bool + operator != (const lldb::SBFrame &rhs) const; + +#endif + + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only); + + lldb::SBValueList + GetRegisters (); + + lldb::SBValue + LookupVar (const char *var_name); + + lldb::SBValue + LookupVarInScope (const char *var_name, const char *scope); + +protected: + friend class SBValue; + + lldb_private::StackFrame * + GetLLDBObjectPtr (); + +private: + friend class SBThread; + +#ifndef SWIG + + lldb_private::StackFrame * + operator->() const; + + // Mimic shared pointer... + lldb_private::StackFrame * + get() const; + +#endif + + + SBFrame (const lldb::StackFrameSP &lldb_object_sp); + + void + SetFrame (const lldb::StackFrameSP &lldb_object_sp); + + lldb::StackFrameSP m_lldb_object_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBFrame_h_ diff --git a/lldb/include/lldb/API/SBFunction.h b/lldb/include/lldb/API/SBFunction.h new file mode 100644 index 000000000000..613d8b0fe092 --- /dev/null +++ b/lldb/include/lldb/API/SBFunction.h @@ -0,0 +1,55 @@ +//===-- 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 + +namespace lldb { + +class SBFunction +{ +public: + + SBFunction (); + + ~SBFunction (); + + bool + IsValid () const; + + const char * + GetName() const; + + const char * + GetMangledName () const; + +#ifndef SWIG + bool + operator == (const lldb::SBFunction &rhs) const; + + bool + operator != (const lldb::SBFunction &rhs) const; +#endif + +private: + friend class SBFrame; + friend class SBSymbolContext; + + SBFunction (lldb_private::Function *lldb_object_ptr); + + + lldb_private::Function *m_lldb_object_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBFunction_h_ diff --git a/lldb/include/lldb/API/SBHostOS.h b/lldb/include/lldb/API/SBHostOS.h new file mode 100644 index 000000000000..53943cac7a6b --- /dev/null +++ b/lldb/include/lldb/API/SBHostOS.h @@ -0,0 +1,54 @@ +//===-- 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 +#include + +namespace lldb { + +class SBHostOS +{ +public: + + static lldb::SBFileSpec + GetProgramFileSpec (); + + 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/lldb/include/lldb/API/SBInputReader.h b/lldb/include/lldb/API/SBInputReader.h new file mode 100644 index 000000000000..5b2590e29b6e --- /dev/null +++ b/lldb/include/lldb/API/SBInputReader.h @@ -0,0 +1,100 @@ +//===-- 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 + +#include + +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 (Callback callback, + void *callback_baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo); + + bool + IsValid () const; + +#ifndef SWIG + const SBInputReader & + operator = (const lldb::SBInputReader &rhs); +#endif + + bool + IsActive () const; + + bool + IsDone () const; + + void + SetIsDone (bool value); + + InputReaderGranularity + GetGranularity (); + +protected: + friend class SBDebugger; + +#ifndef SWIG + + lldb_private::InputReader * + operator->() const; + + lldb::InputReaderSP & + operator *(); + + const lldb::InputReaderSP & + operator *() const; +#endif + + lldb_private::InputReader * + get() 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_reader_sp; + Callback m_callback_function; + void *m_callback_baton; +}; + +} // namespace lldb + +#endif // LLDB_SBInputReader_h_ diff --git a/lldb/include/lldb/API/SBInstruction.h b/lldb/include/lldb/API/SBInstruction.h new file mode 100644 index 000000000000..256b1c017b86 --- /dev/null +++ b/lldb/include/lldb/API/SBInstruction.h @@ -0,0 +1,57 @@ +//===-- 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 + +// There's a lot to be fixed here, but need to wait for underlying insn implementation +// to be revised & settle down first. + +//class lldb_private::Disassembler::Instruction; + +namespace lldb { + +class SBInstruction +{ +public: + + //SBInstruction (lldb_private::Disassembler::Instruction *lldb_insn); + + SBInstruction (); + + ~SBInstruction (); + + //bool + //IsValid(); + + //size_t + //GetByteSize (); + + //void + //SetByteSize (size_t byte_size); + + //bool + //DoesBranch (); + + void + Print (FILE *out); + +private: + + //lldb_private::Disassembler::Instruction::SharedPtr m_lldb_object_sp; + + +}; + + +} // namespace lldb + +#endif // LLDB_SBInstruction_h_ diff --git a/lldb/include/lldb/API/SBInstructionList.h b/lldb/include/lldb/API/SBInstructionList.h new file mode 100644 index 000000000000..455807466fa1 --- /dev/null +++ b/lldb/include/lldb/API/SBInstructionList.h @@ -0,0 +1,53 @@ +//===-- 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 + +namespace lldb { + +class SBInstructionList +{ +public: + + SBInstructionList (); + + ~SBInstructionList (); + + size_t + GetSize (); + + lldb::SBInstruction + GetInstructionAtIndex (uint32_t idx); + + void + Clear (); + + void + AppendInstruction (lldb::SBInstruction inst); + + + void + Print (FILE *out); + +private: + + // If we have an instruction list, it will need to be backed by an + // lldb_private class that contains the list, we can't inherit from + // std::vector here... + //std::vector m_insn_list; + +}; + + +} // namespace lldb + +#endif // LLDB_SBInstructionList_h_ diff --git a/lldb/include/lldb/API/SBLineEntry.h b/lldb/include/lldb/API/SBLineEntry.h new file mode 100644 index 000000000000..2e18bb73cf13 --- /dev/null +++ b/lldb/include/lldb/API/SBLineEntry.h @@ -0,0 +1,88 @@ +//===-- 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 +#include +#include + +namespace lldb { + +class SBLineEntry +{ +public: + + SBLineEntry (); + + SBLineEntry (const lldb::SBLineEntry &rhs); + + ~SBLineEntry (); + +#ifndef SWIG + const lldb::SBLineEntry & + operator = (const lldb::SBLineEntry &rhs); +#endif + + lldb::SBAddress + GetStartAddress () const; + + lldb::SBAddress + GetEndAddress () const; + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + +#ifndef SWIG + bool + operator == (const lldb::SBLineEntry &rhs) const; + + bool + operator != (const lldb::SBLineEntry &rhs) const; + +#endif + +private: + friend class SBCompileUnit; + friend class SBFrame; + friend class SBSymbolContext; + +#ifndef SWIG + + const lldb_private::LineEntry * + operator->() const; + + const lldb_private::LineEntry & + operator*() const; + +#endif + + + SBLineEntry (const lldb_private::LineEntry *lldb_object_ptr); + + void + SetLineEntry (const lldb_private::LineEntry &lldb_object_ref); + + std::auto_ptr m_lldb_object_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBLineEntry_h_ diff --git a/lldb/include/lldb/API/SBListener.h b/lldb/include/lldb/API/SBListener.h new file mode 100644 index 000000000000..8fd28f7635ff --- /dev/null +++ b/lldb/include/lldb/API/SBListener.h @@ -0,0 +1,119 @@ +//===-- 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 + +namespace lldb { + +class SBListener +{ +public: + friend class SBBroadcaster; + friend class SBCommandInterpreter; + friend class SBDebugger; + friend class SBTarget; + + SBListener (const char *name); + + SBListener (lldb_private::Listener &listener); + + SBListener (); + + ~SBListener (); + + void + AddEvent (const lldb::SBEvent &event); + + void + Clear (); + + bool + IsValid () const; + + 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); + +private: + +#ifndef SWIG + + lldb_private::Listener * + operator->() const; + + lldb_private::Listener * + get() const; + + lldb_private::Listener & + operator *(); + + const lldb_private::Listener & + operator *() const; + +#endif + + + + lldb_private::Listener *m_lldb_object_ptr; + bool m_lldb_object_ptr_owned; +}; + +} // namespace lldb + +#endif // LLDB_SBListener_h_ diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h new file mode 100644 index 000000000000..13aa5931a14d --- /dev/null +++ b/lldb/include/lldb/API/SBModule.h @@ -0,0 +1,79 @@ +//===-- 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 + +namespace lldb { + +class SBModule +{ +public: + + SBModule (); + + ~SBModule (); + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + const uint8_t * + GetUUIDBytes () const; + +#ifndef SWIG + bool + operator == (const lldb::SBModule &rhs) const; + + bool + operator != (const lldb::SBModule &rhs) const; + +#endif + + +private: + friend class SBSymbolContext; + friend class SBTarget; + friend class SBFrame; + + explicit SBModule (const lldb::ModuleSP& module_sp); + + void + SetModule (const lldb::ModuleSP& module_sp); +#ifndef SWIG + + lldb::ModuleSP & + operator *(); + + + lldb_private::Module * + operator ->(); + + const lldb_private::Module * + operator ->() const; + + lldb_private::Module * + get(); + + const lldb_private::Module * + get() const; + +#endif + + lldb::ModuleSP m_lldb_object_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBModule_h_ diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h new file mode 100644 index 000000000000..caba20447cda --- /dev/null +++ b/lldb/include/lldb/API/SBProcess.h @@ -0,0 +1,195 @@ +//===-- 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 +#include +#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), + }; + + SBProcess (); + + SBProcess (const lldb::SBProcess& rhs); + + ~SBProcess(); + + void + Clear (); + + bool + IsValid() const; + + lldb::SBTarget + GetTarget() 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; + + void + ReportCurrentState (const lldb::SBEvent &event, FILE *out) const; + + void + AppendCurrentStateReport (const lldb::SBEvent &event, lldb::SBCommandReturnObject &result); + + //------------------------------------------------------------------ + // Thread related functions + //------------------------------------------------------------------ + uint32_t + GetNumThreads (); + + lldb::SBThread + GetThreadAtIndex (size_t index); + + lldb::SBThread + GetThreadByID (lldb::tid_t sb_thread_id); + + lldb::SBThread + GetCurrentThread () const; + + bool + SetCurrentThread (const lldb::SBThread &thread); + + bool + SetCurrentThreadByID (uint32_t tid); + + //------------------------------------------------------------------ + // Stepping related functions + //------------------------------------------------------------------ + + lldb::StateType + GetState (); + + int + GetExitStatus (); + + const char * + GetExitDescription (); + + lldb::pid_t + GetProcessID (); + + uint32_t + GetAddressByteSize() const; + + SBError + Destroy (); + + void + DisplayThreadsInfo (FILE *out = NULL, FILE *err = NULL, bool only_threads_with_stop_reason = true); + + void + ListThreads (); + + bool + WaitUntilProcessHasStopped (lldb::SBCommandReturnObject &result); + + lldb::pid_t + AttachByPID (lldb::pid_t pid); // DEPRECATED: will be removed in a few builds in favor of SBError AttachByPID(pid_t) + + SBError + Attach (lldb::pid_t pid); + + SBError + AttachByName (const char *name, bool wait_for_launch); + + SBError + Continue (); + + SBError + Stop (); + + SBError + Kill (); + + SBError + Detach (); + + SBError + Signal (int signal); + + void + Backtrace (bool all_threads = false, uint32_t num_frames = 0); + + size_t + ReadMemory (addr_t addr, void *buf, size_t size, SBError &error); + + size_t + WriteMemory (addr_t addr, const void *buf, size_t size, SBError &error); + + // Events + static lldb::StateType + GetStateFromEvent (const lldb::SBEvent &event); + + static bool + GetRestartedFromEvent (const lldb::SBEvent &event); + + static lldb::SBProcess + GetProcessFromEvent (const lldb::SBEvent &event); + + lldb::SBBroadcaster + GetBroadcaster () const; + +protected: + friend class SBAddress; + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBCommandInterpreter; + friend class SBDebugger; + friend class SBTarget; + friend class SBThread; + friend class SBValue; + +#ifndef SWIG + + lldb_private::Process * + operator->() const; + + // Mimic shared pointer... + lldb_private::Process * + get() const; + +#endif + + + SBProcess (const lldb::ProcessSP &process_sp); + + void + SetProcess (const lldb::ProcessSP &process_sp); + + lldb::ProcessSP m_lldb_object_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBProcess_h_ diff --git a/lldb/include/lldb/API/SBSourceManager.h b/lldb/include/lldb/API/SBSourceManager.h new file mode 100644 index 000000000000..54ac612ac2ad --- /dev/null +++ b/lldb/include/lldb/API/SBSourceManager.h @@ -0,0 +1,47 @@ +//===-- 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 + +namespace lldb { + +class SBSourceManager +{ +public: + ~SBSourceManager(); + + size_t + DisplaySourceLinesWithLineNumbers (const lldb::SBFileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + FILE *f); + + +protected: + friend class SBCommandInterpreter; + friend class SBDebugger; + + SBSourceManager(lldb_private::SourceManager &source_manager); + + lldb_private::SourceManager & + GetLLDBManager (); + +private: + + lldb_private::SourceManager &m_source_manager; +}; + +} // namespace lldb + +#endif // LLDB_SBSourceManager_h_ diff --git a/lldb/include/lldb/API/SBStringList.h b/lldb/include/lldb/API/SBStringList.h new file mode 100644 index 000000000000..2d1300fe183a --- /dev/null +++ b/lldb/include/lldb/API/SBStringList.h @@ -0,0 +1,72 @@ +//===-- 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 + +namespace lldb { + +class SBStringList +{ +public: + + SBStringList (); + + SBStringList (const lldb_private::StringList *lldb_strings); + + SBStringList (const lldb::SBStringList &rhs); + + ~SBStringList (); + + bool + IsValid() const; + + void + AppendString (const char *str); + + void + AppendList (const char **strv, int strc); + + void + AppendList (lldb::SBStringList strings); + + uint32_t + GetSize () const; + + const char * + GetStringAtIndex (size_t idx); + + void + Clear (); + +#ifndef SWIG + + const lldb_private::StringList * + operator->() const; + + const lldb_private::StringList & + operator*() const; + + const lldb::SBStringList & + operator = (const lldb::SBStringList &rhs); + +#endif + +private: + + std::auto_ptr m_lldb_object_ap; + +}; + +} // namespace lldb + +#endif // LLDB_SBStringList_h_ diff --git a/lldb/include/lldb/API/SBSymbol.h b/lldb/include/lldb/API/SBSymbol.h new file mode 100644 index 000000000000..b7a3058d3c1c --- /dev/null +++ b/lldb/include/lldb/API/SBSymbol.h @@ -0,0 +1,55 @@ +//===-- 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 + +namespace lldb { + +class SBSymbol +{ +public: + + SBSymbol (); + + ~SBSymbol (); + + bool + IsValid () const; + + + const char * + GetName() const; + + const char * + GetMangledName () const; + +#ifndef SWIG + bool + operator == (const lldb::SBSymbol &rhs) const; + + bool + operator != (const lldb::SBSymbol &rhs) const; +#endif + + +private: + friend class SBSymbolContext; + + SBSymbol (lldb_private::Symbol *lldb_object_ptr); + + lldb_private::Symbol *m_lldb_object_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbol_h_ diff --git a/lldb/include/lldb/API/SBSymbolContext.h b/lldb/include/lldb/API/SBSymbolContext.h new file mode 100644 index 000000000000..fd14511883e5 --- /dev/null +++ b/lldb/include/lldb/API/SBSymbolContext.h @@ -0,0 +1,73 @@ +//===-- 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 +#include +#include +#include +#include +#include +#include + +namespace lldb { + +class SBSymbolContext +{ +public: + SBSymbolContext (); + + SBSymbolContext (const lldb::SBSymbolContext& rhs); + + ~SBSymbolContext (); + + bool + IsValid () const; + +#ifndef SWIG + const lldb::SBSymbolContext & + operator = (const lldb::SBSymbolContext &rhs); +#endif + + SBModule GetModule (); + SBCompileUnit GetCompileUnit (); + SBFunction GetFunction (); + SBBlock GetBlock (); + SBLineEntry GetLineEntry (); + SBSymbol GetSymbol (); + +protected: + friend class SBFrame; + friend class SBThread; + +#ifndef SWIG + + lldb_private::SymbolContext* + operator->() const; + +#endif + + lldb_private::SymbolContext * + GetLLDBObjectPtr() const; + + SBSymbolContext (const lldb_private::SymbolContext *sc_ptr); + + void + SetSymbolContext (const lldb_private::SymbolContext *sc_ptr); + +private: + std::auto_ptr m_lldb_object_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbolContext_h_ diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h new file mode 100644 index 000000000000..e950b4cda435 --- /dev/null +++ b/lldb/include/lldb/API/SBTarget.h @@ -0,0 +1,167 @@ +//===-- 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 +#include +#include + +namespace lldb { + +class SBBreakpoint; + +class SBTarget +{ +public: + //------------------------------------------------------------------ + // Broadcaster bits. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2) + }; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SBTarget (const lldb::SBTarget& rhs); + + SBTarget (); // Required for SWIG. + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~SBTarget(); + + const lldb::SBTarget& + Assign (const lldb::SBTarget& rhs); + + bool + IsValid() const; + + lldb::SBProcess + GetProcess (); + + lldb::SBProcess + CreateProcess (); + + lldb::SBProcess + LaunchProcess (char const **argv, + char const **envp, + const char *tty, + bool stop_at_entry); + + lldb::SBFileSpec + GetExecutable (); + + uint32_t + GetNumModules () const; + + lldb::SBModule + GetModuleAtIndex (uint32_t idx); + + lldb::SBModule + FindModule (const lldb::SBFileSpec &file_spec); + + bool + DeleteTargetFromList (lldb_private::TargetList *list); + + bool + MakeCurrentTarget (); + + 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); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateByAddress (addr_t address); + + bool + BreakpointDelete (break_id_t break_id); + + void + ListAllBreakpoints (); + + lldb::SBBreakpoint + FindBreakpointByID (break_id_t break_id); + + bool + EnableAllBreakpoints (); + + bool + DisableAllBreakpoints (); + + bool + DeleteAllBreakpoints (); + + lldb::SBBroadcaster + GetBroadcaster () const; + + //void + //Disassemble (); + + void + Disassemble (lldb::addr_t file_address_start, lldb::addr_t file_address_end = LLDB_INVALID_ADDRESS, + const char *module_name = NULL); + + void + Disassemble (const char *function_name, const char *module_name = NULL); + +#ifndef SWIG + bool + operator == (const lldb::SBTarget &rhs) const; + + bool + operator != (const lldb::SBTarget &rhs) const; + +#endif + +protected: + friend class SBDebugger; + friend class SBProcess; + + //------------------------------------------------------------------ + // Constructors are private, use static Target::Create function to + // create an instance of this class. + //------------------------------------------------------------------ + + SBTarget (const lldb::TargetSP& target_sp); + + void + SetLLBDTarget (const lldb::TargetSP& target_sp); + + lldb_private::Target * + GetLLDBObjectPtr(); + + const lldb_private::Target * + GetLLDBObjectPtr() const; + +private: + //------------------------------------------------------------------ + // For Target only + //------------------------------------------------------------------ + + lldb::TargetSP m_target_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBTarget_h_ diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h new file mode 100644 index 000000000000..220424fd87f7 --- /dev/null +++ b/lldb/include/lldb/API/SBThread.h @@ -0,0 +1,152 @@ +//===-- 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 + +namespace lldb { + +class SBFrame; + +class SBThread +{ +public: + SBThread (); + + SBThread (const lldb::SBThread &thread); + + ~SBThread(); + + bool + IsValid() const; + + lldb::StopReason + GetStopReason(); + + size_t + GetStopDescription (char *dst, size_t dst_len); + + lldb::tid_t + GetThreadID () const; + + uint32_t + GetIndexID () const; + + const char * + GetName () const; + + const char * + GetQueueName() const; + + void + DisplayFramesForCurrentContext (FILE *out, + FILE *err, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source, + uint32_t source_lines_before = 3, + uint32_t source_lines_after = 3); + + bool + DisplaySingleFrameForCurrentContext (FILE *out, + FILE *err, + lldb::SBFrame &frame, + bool show_frame_info, + bool show_source, + uint32_t source_lines_after, + uint32_t source_lines_before); + + void + StepOver (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepOut (); + + void + StepInstruction(bool step_over); + + void + RunToAddress (lldb::addr_t addr); + + void + Backtrace (uint32_t num_frames = 0); + + uint32_t + GetNumFrames (); + + lldb::SBFrame + GetFrameAtIndex (uint32_t idx); + + lldb::SBProcess + GetProcess (); + +#ifndef SWIG + + const lldb::SBThread & + operator = (const lldb::SBThread &rhs); + + bool + operator == (const lldb::SBThread &rhs) const; + + bool + operator != (const lldb::SBThread &rhs) const; + +#endif + + +protected: + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBFrame; + friend class SBProcess; + friend class SBDebugger; + friend class SBValue; + + lldb_private::Thread * + GetLLDBObjectPtr (); + +#ifndef SWIG + + const lldb_private::Thread * + operator->() const; + + const lldb_private::Thread & + operator*() const; + + + lldb_private::Thread * + operator->(); + + lldb_private::Thread & + operator*(); + +#endif + + SBThread (const lldb::ThreadSP& lldb_object_sp); + + void + SetThread (const lldb::ThreadSP& lldb_object_sp); + +private: + //------------------------------------------------------------------ + // Classes that inherit from Thread can see and modify these + //------------------------------------------------------------------ + + lldb::ThreadSP m_lldb_object_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBThread_h_ diff --git a/lldb/include/lldb/API/SBType.h b/lldb/include/lldb/API/SBType.h new file mode 100644 index 000000000000..f647fbf20fc1 --- /dev/null +++ b/lldb/include/lldb/API/SBType.h @@ -0,0 +1,31 @@ +//===-- 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 + +namespace lldb { + +class SBType +{ +public: + + static bool + IsPointerType (void *opaque_type); + +private: + +}; + + +} // namespace lldb + +#endif // LLDB_SBType_h_ diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h new file mode 100644 index 000000000000..395ca00a4f8e --- /dev/null +++ b/lldb/include/lldb/API/SBValue.h @@ -0,0 +1,126 @@ +//===-- 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 + +class lldb_private::Variable; +class lldb_private::ValueObject; +class lldb_private::ExecutionContext; + +namespace lldb { + +class SBValue +{ +public: + SBValue (); + + ~SBValue (); + + bool + IsValid() const; + + void + Print (FILE *out_file, lldb::SBFrame *frame, bool print_type, bool print_value); + + const char * + GetName(); + + const char * + GetTypeName (); + + size_t + GetByteSize (); + + bool + IsInScope (const lldb::SBFrame &frame); + + const char * + GetValue (const lldb::SBFrame &frame); + + bool + GetValueDidChange (); + + const char * + GetSummary (const lldb::SBFrame &frame); + + const char * + GetLocation (const lldb::SBFrame &frame); + + bool + SetValueFromCString (const lldb::SBFrame &frame, const char *value_str); + + lldb::SBValue + GetChildAtIndex (uint32_t idx); + + // 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); + + uint32_t + GetNumChildren (); + + bool + ValueIsStale (); + + void * + GetOpaqueType(); + + //void + //DumpType (); + + lldb::SBValue + Dereference (); + + bool + TypeIsPtrType (); + + +protected: + friend class SBValueList; + friend class SBFrame; + + SBValue (const lldb::ValueObjectSP &value_sp); + +#ifndef SWIG + + // Mimic shared pointer... + lldb_private::ValueObject * + get() const; + + lldb_private::ValueObject * + operator->() const; + + lldb::ValueObjectSP & + operator*(); + + const lldb::ValueObjectSP & + operator*() const; + +#endif + +private: + + lldb_private::ExecutionContext + GetCurrentExecutionContext (); + + lldb::ValueObjectSP m_lldb_object_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBValue_h_ diff --git a/lldb/include/lldb/API/SBValueList.h b/lldb/include/lldb/API/SBValueList.h new file mode 100644 index 000000000000..49b5fef10c75 --- /dev/null +++ b/lldb/include/lldb/API/SBValueList.h @@ -0,0 +1,79 @@ +//===-- 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 + +class lldb_private::ValueObjectList; + +namespace lldb { + +class SBValueList +{ +public: + + SBValueList (); + + SBValueList (const lldb::SBValueList &rhs); + + ~SBValueList(); + + bool + IsValid() const; + + void + Append (const lldb::SBValue &val_obj); + + uint32_t + GetSize() const; + + lldb::SBValue + GetValueAtIndex (uint32_t idx) const; + + lldb::SBValue + FindValueObjectByUID (lldb::user_id_t uid); + + +#ifndef SWIG + const lldb::SBValueList & + operator = (const lldb::SBValueList &rhs); + + lldb_private::ValueObjectList * + operator -> (); + + lldb_private::ValueObjectList & + operator* (); + + const lldb_private::ValueObjectList * + operator -> () const; + + const lldb_private::ValueObjectList & + operator* () const; +#endif + +private: + friend class SBFrame; + + SBValueList (const lldb_private::ValueObjectList *lldb_object_ptr); + + void + Append (lldb::ValueObjectSP& val_obj_sp); + + void + CreateIfNeeded (); + + std::auto_ptr m_lldb_object_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBValueList_h_ diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h new file mode 100644 index 000000000000..1f08636b54ea --- /dev/null +++ b/lldb/include/lldb/Breakpoint/Breakpoint.h @@ -0,0 +1,511 @@ +//===-- 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 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; + + + enum EventSubType + { + eBreakpointInvalidType = (1 << 0), + eBreakpointAdded = (1 << 1), + eBreakpointRemoved = (1 << 2), + eBreakpointLocationsAdded = (1 << 3), + eBreakpointLocationsRemoved = (1 << 4), + eBreakpointLocationResolved = (1 << 5) + }; + + BreakpointEventData (EventSubType sub_type, + lldb::BreakpointSP &new_breakpoint_sp); + + virtual + ~BreakpointEventData(); + + EventSubType + GetSubType () const; + + lldb::BreakpointSP & + GetBreakpoint (); + + + virtual void + Dump (Stream *s) const; + + static BreakpointEventData * + GetEventDataFromEvent (const lldb::EventSP &event_sp); + + static EventSubType + GetSubTypeFromEvent (const lldb::EventSP &event_sp); + + static lldb::BreakpointSP + GetBreakpointFromEvent (const lldb::EventSP &event_sp); + + private: + EventSubType m_sub_type; + 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] changedModules + /// The list of modules to look in for new locations. + //------------------------------------------------------------------ + void + ResolveBreakpointInModules (ModuleList &changedModules); + + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + void + ModulesChanged (ModuleList &changedModules, + bool load_event); + + + //------------------------------------------------------------------ + // 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 (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 (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 (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 (uint32_t index); + + + const lldb::BreakpointSP + GetSP (); + + //------------------------------------------------------------------ + // 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 (int32_t count); + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + int32_t + GetIgnoreCount () 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 (); + + //------------------------------------------------------------------ + /// 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] baton + /// A void * pointer that will get passed back to the callback function. + //------------------------------------------------------------------ + 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 condition expression to be checked when the breakpoint is hit. + /// @param[in] expression + /// The method that will get called when the breakpoint is hit. + //------------------------------------------------------------------ + void + SetCondition (void *expression); + + //------------------------------------------------------------------ + // 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); + + //------------------------------------------------------------------ + /// Accessor for the breakpoint Target. + /// @return + /// This breakpoint's Target. + //------------------------------------------------------------------ + Target & + GetTarget (); + + const Target & + GetTarget () const; + + void + GetResolverDescription (Stream *s); + + 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 (); + + +protected: + friend class Target; + friend class BreakpointLocation; // To call InvokeCallback + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + // Protected Methods + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// 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: + + //------------------------------------------------------------------ + /// Returns the shared pointer that this breakpoint holds for the + /// breakpoint location passed in as \a bp_loc_ptr. Passing in a + /// breakpoint location that doesn't belong to this breakpoint will + /// cause an assert. + /// + /// Meant to be used by the BreakpointLocation::GetSP() function. + /// + /// @return + /// A copy of the shared pointer for the given location. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetLocationSP (BreakpointLocation *bp_loc_ptr); + +private: + //------------------------------------------------------------------ + // For Breakpoint only + //------------------------------------------------------------------ + 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. + + DISALLOW_COPY_AND_ASSIGN(Breakpoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Breakpoint_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointID.h b/lldb/include/lldb/Breakpoint/BreakpointID.h new file mode 100644 index 000000000000..9e352100b9e1 --- /dev/null +++ b/lldb/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/lldb/include/lldb/Breakpoint/BreakpointIDList.h b/lldb/include/lldb/Breakpoint/BreakpointIDList.h new file mode 100644 index 000000000000..bc88087616d0 --- /dev/null +++ b/lldb/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 (); + + int + Size(); + + BreakpointID & + GetBreakpointIDAtIndex (int index); + + bool + RemoveBreakpointIDAtIndex (int index); + + void + Clear(); + + bool + AddBreakpointID (BreakpointID bp_id); + + bool + AddBreakpointID (const char *bp_id); + + bool + FindBreakpointID (BreakpointID &bp_id, int *position); + + bool + FindBreakpointID (const char *bp_id, int *position); + + void + InsertStringArray (const char **string_array, int array_size, CommandReturnObject &result); + + static bool + StringContainsIDRangeExpression (const char *in_string, int *range_start_len, int *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/lldb/include/lldb/Breakpoint/BreakpointList.h b/lldb/include/lldb/Breakpoint/BreakpointList.h new file mode 100644 index 000000000000..225bb3ba498c --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointList.h @@ -0,0 +1,177 @@ +//===-- 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. + //------------------------------------------------------------------ + virtual lldb::break_id_t + Add (lldb::BreakpointSP& bp_sp); + + //------------------------------------------------------------------ + /// 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 + GetBreakpointByIndex (uint32_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 + GetBreakpointByIndex (uint32_t i) const; + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const { 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); + + void + SetEnabledAll (bool enabled); + + //------------------------------------------------------------------ + /// Removes all the breakpoints from this list. + //------------------------------------------------------------------ + void + RemoveAll (); + + //------------------------------------------------------------------ + /// 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 + ClearAllBreakpointSites (); + +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/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h new file mode 100644 index 000000000000..d51184d8760e --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h @@ -0,0 +1,354 @@ +//===-- 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 +#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/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointOptions.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/StringList.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 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 (); + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + int32_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 (int32_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 condition expression to be checked when the breakpoint is hit. + /// + /// @param[in] expression + /// The method that will get called when the breakpoint is hit. + //------------------------------------------------------------------ + void + SetCondition (void *condition); + + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + // 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; + + //------------------------------------------------------------------ + // 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 location specific breakpoint options. + /// + /// @return + /// A pointer to the containing breakpoint's options if this + /// location doesn't have its own copy. + //------------------------------------------------------------------ + BreakpointOptions * + GetOptionsNoCopy (); + +protected: + friend class Breakpoint; + friend class CommandObjectBreakpointCommandAdd; + friend class Process; + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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); + +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, + Address &addr, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID, + bool hardware = false); + + //------------------------------------------------------------------ + // Data members: + //------------------------------------------------------------------ + Address m_address; ///< The address defining this location. + Breakpoint &m_owner; ///< The breakpoint that produced this object. + std::auto_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.) + + DISALLOW_COPY_AND_ASSIGN (BreakpointLocation); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocation_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h b/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h new file mode 100644 index 000000000000..9e04a2c42779 --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h @@ -0,0 +1,187 @@ +//===-- 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::user_id_t break_id, lldb::user_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::user_id_t break_id, lldb::user_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::user_id_t break_id, lldb::user_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 (uint32_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 (uint32_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); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from BreakpointLocationCollection can see + // and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For BreakpointLocationCollection only + //------------------------------------------------------------------ + + typedef std::vector collection; + + collection::iterator + GetIDPairIterator(lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + collection::const_iterator + GetIDPairConstIterator(lldb::user_id_t break_id, lldb::user_id_t break_loc_id) const; + + collection m_break_loc_collection; + +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocationCollection_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocationList.h b/lldb/include/lldb/Breakpoint/BreakpointLocationList.h new file mode 100644 index 000000000000..5c1c8ff51144 --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointLocationList.h @@ -0,0 +1,285 @@ +//===-- 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: + ~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 (Address &addr) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id \a + /// breakID. + /// + /// @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::user_id_t breakID); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + FindByID (lldb::user_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::user_id_t + FindIDByAddress (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 (uint32_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 (uint32_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; + + //------------------------------------------------------------------ + /// Removes the breakpoint location given by \b breakID from this + /// list. + /// + /// @param[in] breakID + /// The breakpoint location index to remove. + /// + /// @result + /// \b true if the breakpoint \a breakID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::user_id_t breakID); + + //------------------------------------------------------------------ + /// 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::user_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(); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + virtual lldb::user_id_t + Add (lldb::BreakpointLocationSP& bp_loc_sp); + + typedef std::vector collection; + typedef std::map addr_map; + + // The breakpoint locations are stored in their Parent Breakpoint's location list by an + // index that is unique to this list, and not across all breakpoint location lists. + // This is only set in the Breakpoint's AddLocation method. + // There is another breakpoint location list, the owner's list in the BreakpointSite, + // but that should not reset the ID. Unfortunately UserID's SetID method is public. + lldb::break_id_t + GetNextID(); + + collection::iterator + GetIDIterator(lldb::user_id_t breakID); + + collection::const_iterator + GetIDConstIterator(lldb::user_id_t breakID) const; + + collection m_locations; + addr_map m_address_to_location; + mutable Mutex m_mutex; + lldb::break_id_t m_next_id; +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocationList_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointOptions.h b/lldb/include/lldb/Breakpoint/BreakpointOptions.h new file mode 100644 index 000000000000..e7a3e3bf1d22 --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointOptions.h @@ -0,0 +1,210 @@ +//===-- 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); + + + //------------------------------------------------------------------ + /// 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); + + virtual ~BreakpointOptions(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const BreakpointOptions& + operator=(const BreakpointOptions& rhs); + + //------------------------------------------------------------------ + // Callbacks + //------------------------------------------------------------------ + void SetCallback (BreakpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false); + bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + bool IsCallbackSynchronous () { + return m_callback_is_synchronous; + }; + Baton *GetBaton (); + void ClearCallback (); + + //------------------------------------------------------------------ + // Enabled/Ignore Count + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled () const; + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetEnabled (bool enabled); + + void + SetIgnoreCount (int32_t n); + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + int32_t + GetIgnoreCount () const; + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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() + { + } + + ~CommandData () + { + } + + StringList user_source; + StringList script_source; + }; + + 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; + int32_t m_ignore_count; // Number of times to ignore this breakpoint + lldb::tid_t m_thread_id; // Thread for which this breakpoint will take + +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointOptions_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolver.h b/lldb/include/lldb/Breakpoint/BreakpointResolver.h new file mode 100644 index 000000000000..1b1284f7970d --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointResolver.h @@ -0,0 +1,123 @@ +//===-- 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/Core/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. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + BreakpointResolver (Breakpoint *bkpt); + + //------------------------------------------------------------------ + /// 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; + +protected: + Target *m_target; // Every resolver has a target. + Breakpoint *m_breakpoint; // This is the breakpoint we add locations to. + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolver_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h b/lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h new file mode 100644 index 000000000000..6807bc0d8dff --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointResolverAddress.h @@ -0,0 +1,68 @@ +//===-- 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; + +protected: + Address m_addr; + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverAddress); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverAddress_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h b/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h new file mode 100644 index 000000000000..285ef525f9f6 --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointResolverFileLine.h @@ -0,0 +1,65 @@ +//===-- 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); + + 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; + +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(BreakpointResolverFileLine); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverFileLine_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolverName.h b/lldb/include/lldb/Breakpoint/BreakpointResolverName.h new file mode 100644 index 000000000000..63e6b86837a7 --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointResolverName.h @@ -0,0 +1,75 @@ +//===-- 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 +// 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 *func_name, + Breakpoint::MatchType type = Breakpoint::Exact); + + // Creates a function breakpoint by regular expression. Takes over control of the lifespan of func_regex. + BreakpointResolverName (Breakpoint *bkpt, + RegularExpression &func_regex); + + BreakpointResolverName (Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type); + + 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; + +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; + Breakpoint::MatchType m_match_type; + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverName); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverName_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointSite.h b/lldb/include/lldb/Breakpoint/BreakpointSite.h new file mode 100644 index 000000000000..aeb385ba18eb --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointSite.h @@ -0,0 +1,258 @@ +//===-- 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 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, + size_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 (lldb::BreakpointLocationSP &owner); + + //------------------------------------------------------------------ + /// This method returns the number of breakpoint locations currently + /// located at this breakpoint site. + /// + /// @return + /// The number of owners. + //------------------------------------------------------------------ + uint32_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 (uint32_t index); + + //------------------------------------------------------------------ + /// 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); + + bool + IsBreakpointAtThisSite (lldb::break_id_t bp_id); + + 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. + //------------------------------------------------------------------ + uint32_t + RemoveOwner (lldb::user_id_t break_id, + lldb::user_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, + lldb::BreakpointLocationSP& owner, + lldb::addr_t m_addr, + lldb::tid_t tid, + bool use_hardware); + + DISALLOW_COPY_AND_ASSIGN(BreakpointSite); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointSite_h_ diff --git a/lldb/include/lldb/Breakpoint/BreakpointSiteList.h b/lldb/include/lldb/Breakpoint/BreakpointSiteList.h new file mode 100644 index 000000000000..bfa8c51e3e85 --- /dev/null +++ b/lldb/include/lldb/Breakpoint/BreakpointSiteList.h @@ -0,0 +1,217 @@ +//===-- 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" + +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::user_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::user_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::user_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::user_id_t + FindIDByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site with index \a i. + /// + /// @param[in] i + /// The breakpoint site index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSiteSP + GetByIndex (uint32_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site with index \a i - const version. + /// + /// @param[in] i + /// The breakpoint site index 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 + GetByIndex (uint32_t i) const; + + //------------------------------------------------------------------ + /// 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::user_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); + + void + SetEnabledForAll(const bool enable, const lldb::user_id_t except_id = LLDB_INVALID_BREAK_ID); + + 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::user_id_t breakID); + + //------------------------------------------------------------------ + /// Returns the number of elements in the list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const { return m_bp_site_list.size(); } + +protected: + typedef std::map collection; + + collection::iterator + GetIDIterator(lldb::user_id_t breakID); + + collection::const_iterator + GetIDConstIterator(lldb::user_id_t breakID) const; + + // This function exposes the m_bp_site_list. I use the in Process because there + // are places there where you want to iterate over the list, and it is less efficient + // to do it by index. FIXME: Find a better way to do this. + + const collection * + GetMap (); + + collection m_bp_site_list; // The breakpoint site list. +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointSiteList_h_ diff --git a/lldb/include/lldb/Breakpoint/Stoppoint.h b/lldb/include/lldb/Breakpoint/Stoppoint.h new file mode 100644 index 000000000000..c294830f15ec --- /dev/null +++ b/lldb/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/lldb/include/lldb/Breakpoint/StoppointCallbackContext.h b/lldb/include/lldb/Breakpoint/StoppointCallbackContext.h new file mode 100644 index 000000000000..f2a6401e7489 --- /dev/null +++ b/lldb/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, Process* process, Thread *thread = NULL, StackFrame * frame = NULL, 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 + ExecutionContext context; // 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/lldb/include/lldb/Breakpoint/StoppointLocation.h b/lldb/include/lldb/Breakpoint/StoppointLocation.h new file mode 100644 index 000000000000..b52551005a6d --- /dev/null +++ b/lldb/include/lldb/Breakpoint/StoppointLocation.h @@ -0,0 +1,111 @@ +//===-- 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, + lldb::tid_t tid, + bool hardware); + + StoppointLocation (lldb::break_id_t bid, + lldb::addr_t m_addr, + lldb::tid_t tid, + size_t size, + bool hardware); + + virtual + ~StoppointLocation (); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + virtual lldb::addr_t + GetLoadAddress () const; + + size_t + GetByteSize () const; + + uint32_t + GetHitCount () const; + + void + IncrementHitCount (); + + uint32_t + GetHardwareIndex () const; + + lldb::tid_t + GetThreadID() const; + + bool + HardwarePreferred () const; + + bool + IsHardware () const; + + virtual bool + ShouldStop (StoppointCallbackContext *context); + + virtual void + Dump (Stream *stream) const; + + void + SetHardwareIndex (uint32_t index); + + lldb::break_id_t + GetID () const; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StoppointLocation can see and modify these + //------------------------------------------------------------------ + lldb::break_id_t m_loc_id; // Break ID + lldb::tid_t m_tid; // The thread ID if this stoppoint location is thread specific, or LLDB_INVALID_THREAD_ID if not thread specific. + 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 has been hit + +private: + //------------------------------------------------------------------ + // For StoppointLocation only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN(StoppointLocation); + StoppointLocation(); // Disallow default constructor +}; + +} // namespace lldb_private + +#endif // liblldb_StoppointLocation_h_ diff --git a/lldb/include/lldb/Breakpoint/WatchpointLocation.h b/lldb/include/lldb/Breakpoint/WatchpointLocation.h new file mode 100644 index 000000000000..9bf559d7166a --- /dev/null +++ b/lldb/include/lldb/Breakpoint/WatchpointLocation.h @@ -0,0 +1,69 @@ +//===-- WatchpointLocation.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WatchpointLocation_h_ +#define liblldb_WatchpointLocation_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" + +namespace lldb_private { + +class WatchpointLocation : + public StoppointLocation +{ +public: + + WatchpointLocation (lldb::addr_t m_addr, lldb::tid_t tid, bool hardware); + + ~WatchpointLocation (); + + bool + IsEnabled () const; + + void + SetEnabled (uint32_t enabled); + + bool WatchpointRead () const; + bool WatchpointWrite () const; + int32_t GetIgnoreCount () const; + void SetIgnoreCount (int32_t n); + void SetWatchpointType (uint32_t type); + bool BreakpointWasHit (StoppointCallbackContext *context); + bool SetCallback (WatchpointHitCallback callback, void *callback_baton); + void Dump (Stream *s) const; + +private: + bool m_enabled; // Is this breakpoint enabled + 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 + int32_t m_ignore_count; // Number of times to ignore this breakpoint + WatchpointHitCallback m_callback; + void * m_callback_baton; // Callback user data to pass to callback + + static lldb::break_id_t + GetNextID(); + + DISALLOW_COPY_AND_ASSIGN (WatchpointLocation); +}; + +} // namespace lldb_private + +#endif // liblldb_WatchpointLocation_h_ diff --git a/lldb/include/lldb/Core/Address.h b/lldb/include/lldb/Core/Address.h new file mode 100644 index 000000000000..14991085ec8a --- /dev/null +++ b/lldb/include/lldb/Core/Address.h @@ -0,0 +1,425 @@ +//===-- 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 +// 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 SymbolContextScope +{ +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 name that an address resolves to + } DumpStyle; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with a invalid section (NULL) and an invalid + /// offset (LLDB_INVALID_ADDRESS). + //------------------------------------------------------------------ + 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); + + //------------------------------------------------------------------ + /// 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 Section* section, lldb::addr_t offset); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Address (); + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// 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, Process *process); + + 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) 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 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 (Process *process) 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; + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// 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); + + bool + IsLinkedAddress () const; + + void + ResolveLinkedAddress (); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + Module * + 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. + //------------------------------------------------------------------ + const Section* + GetSection() const; + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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 Section* section); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + void + CalculateSymbolContext (SymbolContext *sc); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + void + DumpSymbolContext (Stream *s); + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + const Section* m_section; ///< The section for the address, can be NULL. + lldb::addr_t m_offset; ///< Offset into section if \a m_section != NULL, else the absolute address value. +}; + +//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); +bool operator== (const Address& lhs, const Address& rhs); +bool operator!= (const Address& lhs, const Address& rhs); + +//Stream& operator << (Stream& strm, const Address& so_addr); + +} // namespace lldb_private + +#endif // liblldb_Address_h_ diff --git a/lldb/include/lldb/Core/AddressRange.h b/lldb/include/lldb/Core/AddressRange.h new file mode 100644 index 000000000000..2d890327150c --- /dev/null +++ b/lldb/include/lldb/Core/AddressRange.h @@ -0,0 +1,280 @@ +//===-- 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 Section* section, 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, Process *process) 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, Process *process) 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, Process *process, 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(); + + //------------------------------------------------------------------ + /// Get const accessor for the base address of the range. + /// + /// @return + /// A const reference to the base address object. + //------------------------------------------------------------------ + const Address & + GetBaseAddress() const; + + //------------------------------------------------------------------ + /// Get accessor for the byte size of this range. + /// + /// @return + /// The size in bytes of this address range. + //------------------------------------------------------------------ + lldb::addr_t + GetByteSize () const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// 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); + +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/lldb/include/lldb/Core/AddressResolver.h b/lldb/include/lldb/Core/AddressResolver.h new file mode 100644 index 000000000000..3690e7267f75 --- /dev/null +++ b/lldb/include/lldb/Core/AddressResolver.h @@ -0,0 +1,90 @@ +//===-- 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/Core/FileSpec.h" +#include "lldb/Core/RegularExpression.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/lldb/include/lldb/Core/AddressResolverFileLine.h b/lldb/include/lldb/Core/AddressResolverFileLine.h new file mode 100644 index 000000000000..ddeb0e0301d2 --- /dev/null +++ b/lldb/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/lldb/include/lldb/Core/AddressResolverName.h b/lldb/include/lldb/Core/AddressResolverName.h new file mode 100644 index 000000000000..4ab352939ea9 --- /dev/null +++ b/lldb/include/lldb/Core/AddressResolverName.h @@ -0,0 +1,67 @@ +//===-- 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" + +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/lldb/include/lldb/Core/ArchSpec.h b/lldb/include/lldb/Core/ArchSpec.h new file mode 100644 index 000000000000..71b63f6b5b78 --- /dev/null +++ b/lldb/include/lldb/Core/ArchSpec.h @@ -0,0 +1,326 @@ +//===-- 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" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @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, or a +/// string representation. 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: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Default constructor that initializes the object with invalid + /// cpu type and subtype values. + //------------------------------------------------------------------ + ArchSpec (); + + //------------------------------------------------------------------ + /// Constructor with cpu type and subtype. + /// + /// Constructor that initializes the object with supplied cpu and + /// subtypes. + //------------------------------------------------------------------ + ArchSpec (uint32_t cpu, uint32_t sub); + + //------------------------------------------------------------------ + /// Construct with architecture name. + /// + /// Constructor that initializes the object with supplied + /// architecture name. There are also predefined values in + /// Defines.h: + /// @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 32 bit arch the current system defaults to (if any) + /// + /// @li \c LLDB_ARCH_DEFAULT_32BIT + /// The 64 bit arch the current system defaults to (if any) + //------------------------------------------------------------------ + ArchSpec (const char *arch_name); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~ArchSpec (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] rhs another ArchSpec object to copy. + /// + /// @return a const reference to this object + //------------------------------------------------------------------ + const ArchSpec& + operator= (const ArchSpec& rhs); + + //------------------------------------------------------------------ + /// Get a string representation of the contained architecture. + /// + /// Gets a C string representation of the current architecture. + /// If the returned string is a valid architecture name, the string + /// came from a constant string values that do not need to be freed. + /// If the returned string uses the "N.M" format, the string comes + /// from a static buffer that should be copied. + /// + /// @return a NULL terminated C string that does not need to be + /// freed. + //------------------------------------------------------------------ + const char * + AsCString () const; + + //------------------------------------------------------------------ + /// Returns a string representation of the supplied architecture. + /// + /// Class function to get a C string representation given a CPU type + /// and subtype. + /// + /// @param[in] cpu The cpu type of the architecture. + /// @param[in] subtype The cpu subtype of the architecture. + /// + /// @return a NULL terminated C string that does not need to be + /// freed. + //------------------------------------------------------------------ + static const char * + AsCString (uint32_t cpu, uint32_t subtype); + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// CPU subtype get accessor. + /// + /// @return The current value of the CPU subtype. + //------------------------------------------------------------------ + uint32_t + GetCPUSubtype () const; + + //------------------------------------------------------------------ + /// CPU type get accessor. + /// + /// @return The current value of the CPU type. + //------------------------------------------------------------------ + uint32_t + GetCPUType () const; + + //------------------------------------------------------------------ + /// Feature flags get accessor. + /// + /// @return The current value of the CPU feature flags. + //------------------------------------------------------------------ + uint32_t + GetFeatureFlags () const; + + //------------------------------------------------------------------ + /// Get register names of the current architecture. + /// + /// Get register names of the current architecture given + /// a register number, and a flavor for that register number. + /// There are many different register numbering schemes used + /// on a host: + /// @li \c eRegisterKindGCC - gcc compiler register numbering + /// @li \c eRegisterKindDWARF - DWARF register numbering + /// + /// @param[in] reg_num The register number to decode. + /// @param[in] flavor The flavor of the \a reg_num. + /// + /// @return the name of the register as a NULL terminated C string, + /// or /c NULL if the \a reg_num is invalid for \a flavor. + /// String values that are returned do not need to be freed. + //------------------------------------------------------------------ + const char * + GetRegisterName (uint32_t reg_num, uint32_t flavor) const; + + //------------------------------------------------------------------ + /// Get register names for a specified architecture. + /// + /// Get register names of the specified architecture given + /// a register number, and a flavor for that register number. + /// There are many different register numbering schemes used + /// on a host: + /// + /// @li compiler register numbers (@see eRegisterKindGCC) + /// @li DWARF register numbers (@see eRegisterKindDWARF) + /// + /// @param[in] cpu The cpu type of the architecture specific + /// register + /// @param[in] subtype The cpu subtype of the architecture specific + /// register + /// @param[in] reg_num The register number to decode. + /// @param[in] flavor The flavor of the \a reg_num. + /// + /// @return the name of the register as a NULL terminated C string, + /// or /c NULL if the \a reg_num is invalid for \a flavor. + /// String values that are returned do not need to be freed. + //------------------------------------------------------------------ + static const char * + GetRegisterName (uint32_t cpu, uint32_t subtype, uint32_t reg_num, uint32_t flavor); + + //------------------------------------------------------------------ + /// Test if the contained architecture is valid. + /// + /// @return true if the current architecture is valid, false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid () const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize() const; + + //------------------------------------------------------------------ + /// Change the CPU type and subtype given an architecture name. + /// + /// The architecture name supplied can also by 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). + /// + /// @param[in] arch_name The name of an architecture. + /// + /// @return true if \a arch_name was successfully transformed into + /// a valid cpu type and subtype. + //------------------------------------------------------------------ + bool + SetArch (const char *arch_name); + + bool + SetArchFromTargetTriple (const char *arch_name); + //------------------------------------------------------------------ + /// Change the CPU type and subtype given new values of the cpu + /// type and subtype. + /// + /// @param[in] cpu The new CPU type + /// @param[in] subtype The new CPU subtype + //------------------------------------------------------------------ + void + SetArch (uint32_t cpu, uint32_t subtype); + + //------------------------------------------------------------------ + /// Change the CPU subtype given a new value of the CPU subtype. + /// + /// @param[in] subtype The new CPU subtype. + //------------------------------------------------------------------ + void + SetCPUSubtype (uint32_t subtype); + + //------------------------------------------------------------------ + /// Change the CPU type given a new value of the CPU type. + /// + /// @param[in] cpu The new CPU type. + //------------------------------------------------------------------ + void + SetCPUType (uint32_t cpu); + + //------------------------------------------------------------------ + /// Returns the default endianness of the architecture. + /// + /// @return The endian enumeration for the default endianness of + /// the architecture. + //------------------------------------------------------------------ + lldb::ByteOrder + GetDefaultEndian () const; +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + uint32_t m_cpu; ///< The cpu type of the architecture + uint32_t m_sub; ///< The cpu subtype of the architecture +}; + + +//------------------------------------------------------------------ +/// @fn bool operator== (const ArchSpec& lhs, const ArchSpec& rhs) +/// @brief Equal to operator. +/// +/// Tests two ArchSpec objects to see if they are equal. +/// +/// @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 equal to \a rhs +//------------------------------------------------------------------ +bool operator==(const ArchSpec& lhs, const ArchSpec& rhs); + +//------------------------------------------------------------------ +/// @fn bool operator!= (const ArchSpec& lhs, const ArchSpec& rhs) +/// @brief Not equal to operator. +/// +/// Tests two ArchSpec objects to see if they are not equal. +/// +/// @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 not equal to \a rhs +//------------------------------------------------------------------ +bool operator!=(const ArchSpec& lhs, const ArchSpec& rhs); + +//------------------------------------------------------------------ +/// @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/lldb/include/lldb/Core/Args.h b/lldb/include/lldb/Core/Args.h new file mode 100644 index 000000000000..79cb7599defb --- /dev/null +++ b/lldb/include/lldb/Core/Args.h @@ -0,0 +1,368 @@ +//===-- 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.h" +#include "lldb/Core/Error.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { + +typedef std::pair OptionArgPair; +typedef std::vector OptionArgVector; +typedef lldb::SharedPtr::Type OptionArgVectorSP; + +struct OptionArgElement +{ + 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/Core/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); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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); + //------------------------------------------------------------------ + /// 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 (int argc, 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); + + // 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); + + void + ParseArgsForCompletion (Options &options, OptionElementVector &option_element_vector); + + //------------------------------------------------------------------ + // Clear the arguments. + // + // For re-setting or blanking out the list of arguments. + //------------------------------------------------------------------ + void + Clear (); + + 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 lldb::addr_t + StringToAddress (const char *s, lldb::addr_t fail_value = LLDB_INVALID_ADDRESS, bool *success_ptr = NULL); + + static bool + StringToBoolean (const char *s, bool fail_value, bool *success_ptr); + + static int32_t + StringToOptionEnum (const char *s, lldb::OptionEnumValueElement *enum_values, int32_t fail_value, bool *success_ptr); + + static lldb::ScriptLanguage + StringToScriptLanguage (const char *s, lldb::ScriptLanguage fail_value, bool *success_ptr); + + static Error + StringToFormat (const char *s, lldb::Format &format); + + // 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/lldb/include/lldb/Core/Baton.h b/lldb/include/lldb/Core/Baton.h new file mode 100644 index 000000000000..7b9bd87d8d04 --- /dev/null +++ b/lldb/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-include.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/lldb/include/lldb/Core/Broadcaster.h b/lldb/include/lldb/Core/Broadcaster.h new file mode 100644 index 000000000000..c010417335d1 --- /dev/null +++ b/lldb/include/lldb/Core/Broadcaster.h @@ -0,0 +1,194 @@ +//===-- 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 + + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +//#include "lldb/Core/Flags.h" +#include "lldb/Core/Listener.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @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) +/// }; +/// \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 (const char *name); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class gets subclassed. + //------------------------------------------------------------------ + virtual + ~Broadcaster(); + + //------------------------------------------------------------------ + /// 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); + + 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 (); + + 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); + + +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; + // 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. + collection m_broadcaster_listeners; ///< A list of Listener / event_mask pairs that are listening to this broadcaster. + Mutex m_broadcaster_listeners_mutex; ///< A mutex that protects \a m_broadcaster_listeners. + +private: + //------------------------------------------------------------------ + // For Broadcaster only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Broadcaster); +}; + +} // namespace lldb_private + +#endif // liblldb_Broadcaster_h_ diff --git a/lldb/include/lldb/Core/ClangForward.h b/lldb/include/lldb/Core/ClangForward.h new file mode 100644 index 000000000000..3f4a2aee832d --- /dev/null +++ b/lldb/include/lldb/Core/ClangForward.h @@ -0,0 +1,103 @@ +//===-- 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 ASTContext; + class AddrLabelExpr; + class BinaryOperator; + class CodeGenerator; + class CompilerInstance; + class CXXBaseSpecifier; + class CXXBoolLiteralExpr; + class CXXFunctionalCastExpr; + class CXXNamedCastExpr; + class CXXRecordDecl; + class CXXThisExpr; + class CharacterLiteral; + class CompoundAssignOperator; + class Decl; + class DeclaratorDecl; + class DeclContext; + class DeclRefExpr; + class DeclStmt; + class Diagnostic; + class EnumDecl; + class Expr; + class ExtVectorElementExpr; + class FieldDecl; + class FloatingLiteral; + class FunctionDecl; + class GotoStmt; + class IdentifierTable; + class IntegerLiteral; + class LabelStmt; + class LangOptions; + class MemberExpr; + class NamedDecl; + class NamespaceDecl; + class NonTypeTemplateParmDecl; + class ObjCEncodeExpr; + class ObjCImplicitSetterGetterRefExpr; + class ObjCInterfaceDecl; + class ObjCIvarRefExpr; + class ObjCMessageExpr; + class ObjCMethodDecl; + class ObjCPropertyRefExpr; + class ObjCProtocolDecl; + class ObjCProtocolExpr; + class ObjCSelectorExpr; + class ObjCSuperExpr; + class ParenExpr; + class ParmVarDecl; + class PredefinedExpr; + 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 TemplateTemplateParmDecl; + class TemplateTypeParmDecl; + class TextDiagnosticBuffer; + class Type; + class TypedefDecl; + class TypesCompatibleExpr; + class UnaryOperator; + class ValueDecl; + class VarDecl; + struct PrintingPolicy; +} + +#endif // #if defined(__cplusplus) +#endif // liblldb_ClangForward_h_ diff --git a/lldb/include/lldb/Core/Communication.h b/lldb/include/lldb/Core/Communication.h new file mode 100644 index 000000000000..dc8bfba4d924 --- /dev/null +++ b/lldb/include/lldb/Core/Communication.h @@ -0,0 +1,401 @@ +//===-- 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/Host.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.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 (); + + //------------------------------------------------------------------ + /// Poll for bytes available if the communications supports it. + /// + /// @param[in] timeout_usec + /// A timeout value in micro-seconds, UINT32_MAX for infinite + /// wait. + /// + /// @return + /// \b True if the bytes are, or became available within the + /// timeout period, \b false otherwise. + //------------------------------------------------------------------ + virtual lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, Error *error_ptr); + + //------------------------------------------------------------------ + /// 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; + //------------------------------------------------------------------ + /// Read bytes from the current connection. + /// + /// If no read thread is running, this function call the + /// connection's Connection::BytesAvailable(uint32_t) method to + /// wait for available data, and then call the + /// Connection::Read(void *, size_t) function to get any available + /// bytes if Connection::BytesAvailable(uint32_t) returned true. + /// + /// 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 bool Connection::BytesAvailable (uint32_t); + /// @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); + +private: + //------------------------------------------------------------------ + // For Communication only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Communication); + +protected: + std::auto_ptr m_connection_ap; ///< 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. + ReadThreadBytesReceived m_callback; + void *m_callback_baton; + + size_t + ReadFromConnection (void *dst, + size_t dst_len, + 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); + + //------------------------------------------------------------------ + /// 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/lldb/include/lldb/Core/Connection.h b/lldb/include/lldb/Core/Connection.h new file mode 100644 index 000000000000..ca16930e8d07 --- /dev/null +++ b/lldb/include/lldb/Core/Connection.h @@ -0,0 +1,176 @@ +//===-- 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 (); + + //------------------------------------------------------------------ + /// Poll for bytes available if the communications supports it. + /// + /// @param[in] timeout_usec + /// A timeout value in micro-seconds. + /// + /// @param[out] error + /// A reference 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 bytes are, or became available within the + /// timeout period, \b false otherwise. + //------------------------------------------------------------------ + virtual lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// 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, 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/lldb/include/lldb/Core/ConnectionFileDescriptor.h b/lldb/include/lldb/Core/ConnectionFileDescriptor.h new file mode 100644 index 000000000000..53a3997eab77 --- /dev/null +++ b/lldb/include/lldb/Core/ConnectionFileDescriptor.h @@ -0,0 +1,74 @@ +//===-- 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 +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.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 + 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, lldb::ConnectionStatus &status, Error *error_ptr); + + virtual size_t + Write (const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr); + +protected: + lldb::ConnectionStatus + SocketListen (uint16_t listen_port_num, Error *error_ptr); + + lldb::ConnectionStatus + SocketConnect (const char *host_and_port, Error *error_ptr); + + lldb::ConnectionStatus + Close (int& fd, Error *error); + + int m_fd; // Socket we use to communicate once conn established + bool m_is_socket; + bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. + + static int + SetSocketOption(int fd, int level, int option_name, int option_value); + +private: + DISALLOW_COPY_AND_ASSIGN (ConnectionFileDescriptor); +}; + +} // namespace lldb_private + +#endif // liblldb_ConnectionFileDescriptor_h_ diff --git a/lldb/include/lldb/Core/ConstString.h b/lldb/include/lldb/Core/ConstString.h new file mode 100644 index 000000000000..9d95b96945be --- /dev/null +++ b/lldb/include/lldb/Core/ConstString.h @@ -0,0 +1,396 @@ +//===-- 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" + +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 ref counted +/// strings. Since the strings are uniqued, finding strings that are +/// equal to one another is very fast (pointer compares). It also allows +/// for many common strings from many different sources to be shared to +/// keep the memory footprint low. +//---------------------------------------------------------------------- +class ConstString +{ +public: + //------------------------------------------------------------------ + /// Default constructor + /// + /// Initializes the string to an empty string. + //------------------------------------------------------------------ + ConstString (); + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Copies the string value in \a rhs and retains an extra reference + /// to the string value in the string pool. + /// + /// @param[in] rhs + /// Another string object to copy. + //------------------------------------------------------------------ + ConstString (const ConstString& rhs); + + //------------------------------------------------------------------ + /// 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 does + /// exist, it retains an extra reference to the string in the string + /// pool. If it doesn't exist, it is added to the string pool with + /// a reference count of 1. + /// + /// @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. + /// + /// If the C string already exists in the global string pool, it + /// retains an extra reference to the string in the string + /// pool. If it doesn't exist, it is added to the string pool with + /// a reference count of 1. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + /// + /// @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 + /// + /// Decrements the reference count on the contained string, and if + /// the resulting reference count is zero, then the string is removed + /// from the global string pool. If the reference count is still + /// greater than zero, the string will remain in the string pool + /// until the last reference is released by other ConstString objects. + //------------------------------------------------------------------ + ~ConstString (); + + //---------------------------------------------------------------------- + /// C string equality function object for CStrings contains in the + /// same StringPool only. (binary predicate). + //---------------------------------------------------------------------- + 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 pointer 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 + /// A pointer to this object if the string isn't empty, NULL + /// otherwise. + //------------------------------------------------------------------ + operator void*() const; + + + //------------------------------------------------------------------ + /// Assignment operator + /// + /// Assigns the string in this object with the value from \a rhs + /// and increments the reference count of that string. + /// + /// The previously contained string will be get its reference count + /// decremented and removed from the string pool if its reference + /// count reaches zero. + /// + /// @param[in] rhs + /// Another string object to copy into this object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ConstString& + operator = (const ConstString& rhs); + + //------------------------------------------------------------------ + /// 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 and reference + /// counted 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; + + //------------------------------------------------------------------ + /// 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 and reference + /// counted 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; + + 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; + + + const char * + GetCString () const; + + size_t + GetLength () const; + //------------------------------------------------------------------ + /// Clear this object's state. + /// + /// Clear any contained string and reset the value to the an empty + /// string value. + /// + /// The previously contained string will be get its reference count + /// decremented and removed from the string pool if its reference + /// count reaches zero. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Compare two string objects. + /// + /// Compares the C string values contained in \a lhs and \a rhs and + /// returns an integer result. + /// + /// @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. + /// + /// Dump the string value and the reference count to the stream \a + /// s. + /// + /// @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; + + //------------------------------------------------------------------ + /// 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 retains an extra reference to the + /// string in the string pool. If it doesn't exist, it is added to + /// the string pool with a reference count of 1. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + //------------------------------------------------------------------ + void + SetCString (const char *cstr); + + //------------------------------------------------------------------ + /// 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 trim is false, then this allows strings + /// with NULL characters to be added to the string pool. + /// + /// If the C string already exists in the global string pool, it + /// retains an extra reference to the string in the string + /// pool. If it doesn't exist, it is added to the string pool with + /// a reference count of 1. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + /// + /// @param[in] cstr_len + /// The absolute length of the C string if \a trim is false, + /// or the maximum length of the C string if \a trim is true. + /// + /// @param[in] trim + /// If \b true, trim \a cstr to it's actual length before adding + /// it to the string pool. If \b false then cstr_len is the + /// actual length of the C string to add. + //------------------------------------------------------------------ + 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; + + //------------------------------------------------------------------ + /// 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 reference count 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/lldb/include/lldb/Core/DataBuffer.h b/lldb/include/lldb/Core/DataBuffer.h new file mode 100644 index 000000000000..9acaea6cb5bd --- /dev/null +++ b/lldb/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 size_t + GetByteSize() const = 0; +}; + +} // namespace lldb_private + +#endif /// #if defined(__cplusplus) +#endif /// lldb_DataBuffer_h_ diff --git a/lldb/include/lldb/Core/DataBufferHeap.h b/lldb/include/lldb/Core/DataBufferHeap.h new file mode 100644 index 000000000000..99fa2e570870 --- /dev/null +++ b/lldb/include/lldb/Core/DataBufferHeap.h @@ -0,0 +1,136 @@ +//===-- 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 (size_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, size_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 size_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. + //------------------------------------------------------------------ + size_t + SetByteSize (size_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, size_t src_len); + +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/lldb/include/lldb/Core/DataBufferMemoryMap.h b/lldb/include/lldb/Core/DataBufferMemoryMap.h new file mode 100644 index 000000000000..910601537eb5 --- /dev/null +++ b/lldb/include/lldb/Core/DataBufferMemoryMap.h @@ -0,0 +1,156 @@ +//===-- 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 size_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_T_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_T_MAX, map + /// as many bytes as possible. + /// + /// @return + /// The number of bytes mapped starting from the \a offset. + //------------------------------------------------------------------ + size_t + MemoryMapFromFileSpec (const FileSpec* file, + off_t offset = 0, + size_t length = SIZE_T_MAX); + + //------------------------------------------------------------------ + /// 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_T_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_T_MAX, map + /// as many bytes as possible. + /// + /// @return + /// The number of bytes mapped starting from the \a offset. + //------------------------------------------------------------------ + size_t + MemoryMapFromFileDescriptor (int fd, off_t offset = 0, size_t length = SIZE_T_MAX); + +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. + size_t m_size; ///< The size of the data the user got when data was requested + Error m_error; ///< An error object that describes any errors that occurred during the memory mapping process + +private: + DISALLOW_COPY_AND_ASSIGN (DataBufferMemoryMap); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DataBufferMemoryMap_h_ diff --git a/lldb/include/lldb/Core/DataExtractor.h b/lldb/include/lldb/Core/DataExtractor.h new file mode 100644 index 000000000000..b6ac6e93d089 --- /dev/null +++ b/lldb/include/lldb/Core/DataExtractor.h @@ -0,0 +1,1124 @@ +//===-- 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; + + //------------------------------------------------------------------ + /// 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, 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. + //------------------------------------------------------------------ + DataExtractor (lldb::DataBufferSP& data_sp, lldb::ByteOrder byte_order, uint8_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, uint32_t offset = 0, uint32_t length = UINT32_MAX); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + uint32_t + PutToLog (Log *log, + uint32_t offset, + uint32_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. + //------------------------------------------------------------------ + uint32_t + Dump(Stream *s, + uint32_t offset, + lldb::Format item_format, + uint32_t item_byte_size, + uint32_t item_count, + uint32_t num_per_line, + uint64_t base_addr, + uint32_t item_bit_size, + uint32_t item_bit_offset) 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, uint32_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 (uint32_t offset, uint32_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 (uint32_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. + //------------------------------------------------------------------ + uint8_t + GetAddressByteSize () const; + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// 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 (uint32_t *offset_ptr) 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 (uint32_t *offset_ptr, uint32_t length) 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; + + //------------------------------------------------------------------ + /// 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; + + + //------------------------------------------------------------------ + /// 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 integer value that was extracted, or zero on failure. + //------------------------------------------------------------------ + float + GetFloat (uint32_t *offset_ptr) const; + + double + GetDouble (uint32_t *offset_ptr) const; + + long double + GetLongDouble (uint32_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 (uint32_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 (uint32_t *offset_ptr, uint32_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 (uint32_t *offset_ptr, uint32_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 (uint32_t *offset_ptr, uint32_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 (uint32_t *offset_ptr, uint32_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 (uint32_t *offset_ptr, uint32_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 (uint32_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; + + //------------------------------------------------------------------ + /// 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 ( uint32_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 ( uint32_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 (uint32_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 (uint32_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 (uint32_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 (uint32_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 (uint32_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 ( uint32_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 (uint32_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 (uint32_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// 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 (uint32_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 (uint32_t offset, uint32_t length) const; + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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 \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. + //------------------------------------------------------------------ + uint32_t + SetData (const DataExtractor& data, uint32_t offset = 0, uint32_t length = UINT_MAX); + + //------------------------------------------------------------------ + /// 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 (lldb::DataBufferSP& data_sp, uint32_t offset = 0, uint32_t length = UINT_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); + + //------------------------------------------------------------------ + /// 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 (uint32_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 (uint32_t offset) const; + + //------------------------------------------------------------------ + /// 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; + +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. + 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 +}; + +} // namespace lldb_private + +#endif // #if defined (__cplusplus) +#endif // #ifndef liblldb_DataExtractor_h_ diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h new file mode 100644 index 000000000000..c5ae8842362c --- /dev/null +++ b/lldb/include/lldb/Core/Debugger.h @@ -0,0 +1,170 @@ +//===-- 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/Interpreter/CommandInterpreter.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/SourceManager.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: + + static void + Initialize (); + + static void + Terminate (); + + static Debugger & + GetSharedInstance (); + + ~Debugger (); + + bool + GetAsyncExecution (); + + void + SetAsyncExecution (bool async); + + void + SetInputFileHandle (FILE *fh, bool tranfer_ownership); + + void + SetOutputFileHandle (FILE *fh, bool tranfer_ownership); + + void + SetErrorFileHandle (FILE *fh, bool tranfer_ownership); + + FILE * + GetInputFileHandle (); + + FILE * + GetOutputFileHandle (); + + FILE * + GetErrorFileHandle (); + + Stream& + GetOutputStream () + { + return m_output_file; + } + + Stream& + GetErrorStream () + { + return m_error_file; + } + + CommandInterpreter & + GetCommandInterpreter (); + + Listener & + GetListener (); + + SourceManager & + GetSourceManager (); + + lldb::TargetSP + GetCurrentTarget (); + + ExecutionContext + GetCurrentExecutionContext(); + //------------------------------------------------------------------ + /// 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 (); + + 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); + +protected: + + static void + DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len); + + void + ActivateInputReader (const lldb::InputReaderSP &reader_sp); + + bool + CheckIfTopInputReaderIsDone (); + + void + DisconnectInput(); + + bool m_async_execution; + Communication m_input_comm; + StreamFile m_input_file; + StreamFile m_output_file; + StreamFile m_error_file; + TargetList m_target_list; + Listener m_listener; + SourceManager m_source_manager; + CommandInterpreter m_command_interpreter; + + std::stack m_input_readers; + std::string m_input_reader_data; + + typedef std::tr1::shared_ptr DebuggerSP; + + static DebuggerSP & + GetDebuggerSP(); + + static int g_shared_debugger_refcount; + static bool g_in_terminate; + +private: + Debugger (); // Access the single global instance of this class using Debugger::GetSharedInstance(); + + DISALLOW_COPY_AND_ASSIGN (Debugger); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Debugger_h_ diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h new file mode 100644 index 000000000000..33d7d561dece --- /dev/null +++ b/lldb/include/lldb/Core/Disassembler.h @@ -0,0 +1,138 @@ +//===-- 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 + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +class ExecutionContext; + +class Disassembler : + public PluginInterface +{ +public: + class Instruction + { + public: + typedef lldb::SharedPtr::Type shared_ptr; + + Instruction(); + + virtual + ~Instruction(); + + virtual size_t + GetByteSize() const = 0; + + virtual void + Dump (Stream *s, lldb::addr_t base_address, DataExtractor *bytes, uint32_t bytes_offset, const lldb_private::ExecutionContext exe_ctx, bool raw) = 0; + + virtual bool + DoesBranch () const = 0; + + virtual size_t + Extract (const DataExtractor& data, uint32_t data_offset) = 0; + }; + + + class InstructionList + { + public: + InstructionList(); + ~InstructionList(); + + size_t + GetSize() const; + + Instruction * + GetInstructionAtIndex (uint32_t idx); + + const Instruction * + GetInstructionAtIndex (uint32_t idx) const; + + void + Clear(); + + void + AppendInstruction (Instruction::shared_ptr &inst_sp); + + private: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_instructions; + }; + + + static Disassembler* + FindPlugin (const ArchSpec &arch); + + static bool + Disassemble (const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t mixed_context_lines, + Stream &strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Disassembler(const ArchSpec &arch); + virtual ~Disassembler(); + + typedef const char * (*SummaryCallback)(const Instruction& inst, ExecutionContext *exe_context, void *user_data); + + size_t + ParseInstructions (const ExecutionContext *exe_ctx, + lldb::AddressType addr_type, + lldb::addr_t addr, + size_t byte_size, + DataExtractor& data); + + virtual size_t + ParseInstructions (const DataExtractor& data, + uint32_t data_offset, + uint32_t num_instructions, + lldb::addr_t base_addr) = 0; + + InstructionList & + GetInstructionList (); + + const InstructionList & + GetInstructionList () const; + +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; + +private: + //------------------------------------------------------------------ + // For Disassembler only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Disassembler); +}; + +} // namespace lldb_private + +#endif // liblldb_Disassembler_h_ diff --git a/lldb/include/lldb/Core/Error.h b/lldb/include/lldb/Core/Error.h new file mode 100644 index 000000000000..d4b8bab2383a --- /dev/null +++ b/lldb/include/lldb/Core/Error.h @@ -0,0 +1,291 @@ +//===-- 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) + +#include +#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. + //------------------------------------------------------------------ + explicit + Error (ValueType err = 0, lldb::ErrorType type = lldb::eErrorTypeGeneric); + + //------------------------------------------------------------------ + /// 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 = (kern_return_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, ...); + + //------------------------------------------------------------------ + /// 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, ...); + + //------------------------------------------------------------------ + /// 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 + SetError (kern_return_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, ...); + + 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; + +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/lldb/include/lldb/Core/Event.h b/lldb/include/lldb/Core/Event.h new file mode 100644 index 000000000000..66c5fbd7afae --- /dev/null +++ b/lldb/include/lldb/Core/Event.h @@ -0,0 +1,180 @@ +//===-- 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 + 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 (); + + const EventData * + GetData () const; + + uint32_t + GetType () const; + + Broadcaster * + GetBroadcaster () const; + + bool + BroadcasterIs (Broadcaster *broadcaster); + + void + Clear(); + + +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); + + Broadcaster * m_broadcaster; // The broadcaster that sent this event + uint32_t m_type; // The bit describing this event + std::auto_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/lldb/include/lldb/Core/FileSpec.h b/lldb/include/lldb/Core/FileSpec.h new file mode 100644 index 000000000000..4aa17826c2b2 --- /dev/null +++ b/lldb/include/lldb/Core/FileSpec.h @@ -0,0 +1,440 @@ +//===-- 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/Core/FileSpec.h" +/// @brief A file utility class. +/// +/// A file specification class that divides paths up into a directory +/// and filename. These string values of the paths are put into uniqued +/// string pools for fast comparisons and efficient memory usage. +//---------------------------------------------------------------------- +class FileSpec +{ +public: + typedef enum FileType + { + eFileTypeInvalid = -1, + eFileTypeUknown = 0, + eFileTypeDirectory, + eFileTypePipe, + eFileTypeRegular, + eFileTypeSocket, + eFileTypeSymbolicLink, + }; + + FileSpec(); + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Takes an optional full path to a file. If \a path is valid, + /// this function will call FileSpec::SetFile (\a path). + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @see FileSpec::SetFile () + //------------------------------------------------------------------ + explicit FileSpec (const char *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. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~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 + void* () 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; + + 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; + + 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 or \a path. + /// + /// @return + /// \b true if the extracted fullpath fits into \a path, \b + /// false otherwise. + //------------------------------------------------------------------ + bool + GetPath (char *path, size_t max_path_length) const; + + FileType + GetFileType () 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; + + //------------------------------------------------------------------ + /// 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_T_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_T_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_T_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_T_MAX) const; + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + void + SetFile (const char *path); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + bool + ReadFileLines (STLStringArray &lines); + + static int + Resolve (const char *src_path, char *dst_path, size_t dst_len); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + ConstString m_directory; ///< The uniqued directory path + ConstString m_filename; ///< The uniqued filename path +}; + +//---------------------------------------------------------------------- +/// 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/lldb/include/lldb/Core/FileSpecList.h b/lldb/include/lldb/Core/FileSpecList.h new file mode 100644 index 000000000000..c573bf847bce --- /dev/null +++ b/lldb/include/lldb/Core/FileSpecList.h @@ -0,0 +1,196 @@ +//===-- 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/Core/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; + + //------------------------------------------------------------------ + /// 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. + /// + /// @return + /// The index of the file that matches \a file if it is found, + /// else UINT32_MAX is returned. + //------------------------------------------------------------------ + uint32_t + FindFileIndex (uint32_t idx, const FileSpec &file) 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 (uint32_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 (uint32_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; + + //------------------------------------------------------------------ + /// Get the number of files in the file list. + /// + /// @return + /// The number of files in the file spec list. + //------------------------------------------------------------------ + uint32_t + GetSize () const; + + 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/lldb/include/lldb/Core/Flags.h b/lldb/include/lldb/Core/Flags.h new file mode 100644 index 000000000000..92452f3dfd08 --- /dev/null +++ b/lldb/include/lldb/Core/Flags.h @@ -0,0 +1,153 @@ +//===-- 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 flag bits. +/// +/// The Flags class does bits. +//---------------------------------------------------------------------- +class Flags +{ +public: + //---------------------------------------------------------------------- + /// The value type for flag bits is a 32 bit unsigned integer type. + //---------------------------------------------------------------------- + typedef uint32_t ValueType; + + //---------------------------------------------------------------------- + /// Construct with initial flag bit values. + /// + /// Constructs this object with \a bits as the initial value for all + /// of the flag bits. + /// + /// @param[in] bits + /// The initial value for all flag bits. + //---------------------------------------------------------------------- + Flags (ValueType bits = 0); + + //---------------------------------------------------------------------- + /// Copy constructor. + /// + /// Construct and copy the flag bits from \a rhs. + /// + /// @param[in] rhs + /// A const Flags object reference to copy. + //---------------------------------------------------------------------- + Flags (const Flags& rhs); + + //---------------------------------------------------------------------- + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //---------------------------------------------------------------------- + virtual + ~Flags (); + + //---------------------------------------------------------------------- + /// Get accessor for all flag bits. + /// + /// @return + /// Returns all of the flag bits as a Flags::ValueType. + //---------------------------------------------------------------------- + ValueType + GetAllFlagBits () const; + + size_t + GetBitSize() const; + + //---------------------------------------------------------------------- + /// Set accessor for all flag bits. + /// + /// @param[in] bits + /// The bits with which to replace all of the current flag bits. + //---------------------------------------------------------------------- + void + SetAllFlagBits (ValueType bits); + + //---------------------------------------------------------------------- + /// Clear one or more flag bits. + /// + /// @param[in] bits + /// A bitfield containing one or more flag bits. + /// + /// @return + /// The new flag bits after clearing all bits from \a bits. + //---------------------------------------------------------------------- + ValueType + Clear (ValueType bits); + + //---------------------------------------------------------------------- + /// Set one or more flag bits. + /// + /// @param[in] bits + /// A bitfield containing one or more flag bits. + /// + /// @return + /// The new flag bits after setting all bits from \a bits. + //---------------------------------------------------------------------- + ValueType + Set (ValueType bits); + + //---------------------------------------------------------------------- + /// Test one or more flag bits. + /// + /// @return + /// \b true if \b any flag bits in \a bits are set, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + IsSet (ValueType bits) const; + + //---------------------------------------------------------------------- + /// Test one or more flag bits. + /// + /// @return + /// \b true if \b all flag bits in \a bits are clear, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + IsClear (ValueType bits) const; + + //---------------------------------------------------------------------- + /// 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; + + //---------------------------------------------------------------------- + /// 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; + +protected: + ValueType m_flags; ///< The flag bits. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Flags_h_ diff --git a/lldb/include/lldb/Core/IOStreamMacros.h b/lldb/include/lldb/Core/IOStreamMacros.h new file mode 100644 index 000000000000..cc4eeb0e050f --- /dev/null +++ b/lldb/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/lldb/include/lldb/Core/InputReader.h b/lldb/include/lldb/Core/InputReader.h new file mode 100644 index 000000000000..2d876240c1d7 --- /dev/null +++ b/lldb/include/lldb/Core/InputReader.h @@ -0,0 +1,114 @@ +//===-- 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-include.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/Debugger.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); + + InputReader (); + + virtual + ~InputReader (); + + virtual Error + Initialize (Callback callback, + void *baton, + lldb::InputReaderGranularity token_size, + const char *end_token, + const char *prompt, + bool echo); + + 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; + } + + // 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); + + FILE * + GetInputFileHandle (); + + FILE * + GetOutputFileHandle (); + + bool + IsActive () const + { + return m_active; + } + + const char * + GetPrompt () const; + + void + RefreshPrompt(); + +protected: + friend class Debugger; + + void + Notify (lldb::InputReaderAction notification); + + 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; + +private: + DISALLOW_COPY_AND_ASSIGN (InputReader); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_InputReader_h_ diff --git a/lldb/include/lldb/Core/Language.h b/lldb/include/lldb/Core/Language.h new file mode 100644 index 000000000000..f086cae3726d --- /dev/null +++ b/lldb/include/lldb/Core/Language.h @@ -0,0 +1,149 @@ +//===-- 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: + + //------------------------------------------------------------------ + /// Programming language type. + /// + /// These enumerations use the same language enumerations as the + /// DWARF specification for ease of use and consistency. + //------------------------------------------------------------------ + typedef enum + { + Unknown = 0x0000, ///< Unknown or invalid language value. + C89 = 0x0001, ///< ISO C:1989. + C = 0x0002, ///< Non-standardized C, such as K&R. + Ada83 = 0x0003, ///< ISO Ada:1983. + C_plus_plus = 0x0004, ///< ISO C++:1998. + Cobol74 = 0x0005, ///< ISO Cobol:1974. + Cobol85 = 0x0006, ///< ISO Cobol:1985. + Fortran77 = 0x0007, ///< ISO Fortran 77. + Fortran90 = 0x0008, ///< ISO Fortran 90. + Pascal83 = 0x0009, ///< ISO Pascal:1983. + Modula2 = 0x000a, ///< ISO Modula-2:1996. + Java = 0x000b, ///< Java. + C99 = 0x000c, ///< ISO C:1999. + Ada95 = 0x000d, ///< ISO Ada:1995. + Fortran95 = 0x000e, ///< ISO Fortran 95. + PLI = 0x000f, ///< ANSI PL/I:1976. + ObjC = 0x0010, ///< Objective-C. + ObjC_plus_plus = 0x0011, ///< Objective-C++. + UPC = 0x0012, ///< Unified Parallel C. + D = 0x0013, ///< D. + Python = 0x0014, ///< Python. + } Type; + + //------------------------------------------------------------------ + /// Construct with optional language enumeration. + //------------------------------------------------------------------ + Language(Language::Type language = Unknown); + + //------------------------------------------------------------------ + /// 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 Language::Type + /// 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. + //------------------------------------------------------------------ + Language::Type + 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(Language::Type 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 + //------------------------------------------------------------------ + Language::Type 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/lldb/include/lldb/Core/Listener.h b/lldb/include/lldb/Core/Listener.h new file mode 100644 index 000000000000..a47e10122393 --- /dev/null +++ b/lldb/include/lldb/Core/Listener.h @@ -0,0 +1,173 @@ +//===-- 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 + + +// 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; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Listener (const char *name); + + ~Listener (); + + void + AddEvent (lldb::EventSP &event); + + void + Clear (); + + 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); + +protected: + + //------------------------------------------------------------------ + // 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; + + 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; + + void + BroadcasterWillDestruct (Broadcaster *); +private: + +// 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/lldb/include/lldb/Core/Log.h b/lldb/include/lldb/Core/Log.h new file mode 100644 index 000000000000..78017bb167e0 --- /dev/null +++ b/lldb/include/lldb/Core/Log.h @@ -0,0 +1,243 @@ +//===-- 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 +#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) + +//---------------------------------------------------------------------- +// Logging Functions +//---------------------------------------------------------------------- +namespace lldb_private { + +class Log +{ +public: + + //------------------------------------------------------------------ + // Callback definitions for abstracted plug-in log access. + //------------------------------------------------------------------ + typedef void (*DisableCallback) (); + typedef Log* (*EnableCallback) (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + Args &args, + Stream *feedback_strm); + typedef void (*ListCategoriesCallback) (Stream *strm); + + typedef struct Callbacks + { + DisableCallback disable; + EnableCallback enable; + ListCategoriesCallback list_categories; + }; + + //------------------------------------------------------------------ + // Static accessors for logging channels + //------------------------------------------------------------------ + static void + RegisterLogChannel (const char *channel, + const Log::Callbacks &log_callbacks); + + static bool + UnregisterLogChannel (const char *channel); + + static bool + GetLogChannelCallbacks (const char *channel, + Log::Callbacks &log_callbacks); + + + static void + EnableAllLogChannels (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + Args &args, + Stream *feedback_strm); + + static void + DisableAllLogChannels (); + + static void + ListAllLogChannels (Stream *strm); + + //------------------------------------------------------------------ + // Static accessors to STDOUT logging facilities. + //------------------------------------------------------------------ + static void + STDOUT (const char *format, ...); + + static lldb::StreamSP + GetStreamForSTDOUT (); + + static void + SetStreamForSTDOUT (lldb::StreamSP &stream_sp); + + //------------------------------------------------------------------ + // Static accessors to STDERR logging facilities. + //------------------------------------------------------------------ + static void + STDERR (const char *format, ...); + + static lldb::StreamSP + GetStreamForSTDERR (); + + static void + SetStreamForSTDERR (lldb::StreamSP &stream_sp); + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + Log (); + + Log (lldb::StreamSP &stream_sp); + + ~Log (); + + void + PutCString (const char *cstr); + + void + Printf (const char *format, ...); + + void + VAPrintf (const char *format, va_list args); + + void + PrintfWithFlags( uint32_t flags, const char *format, ...); + + void + LogIf (uint32_t mask, const char *fmt, ...); + + void + Debug (const char *fmt, ...); + + void + DebugVerbose (const char *fmt, ...); + + void + Error (const char *fmt, ...); + + void + FatalError (int err, const char *fmt, ...); + + void + Verbose (const char *fmt, ...); + + void + Warning (const char *fmt, ...); + + void + WarningVerbose (const char *fmt, ...); + + Flags & + GetOptions(); + + const Flags & + GetOptions() const; + + Flags & + GetMask(); + + const Flags & + GetMask() const; + + bool + GetVerbose() const; + + bool + GetDebug() const; + +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 const char * + GetPluginSuffix (); + + static lldb::LogChannelSP + FindPlugin (const char *plugin_name); + + virtual void + Disable () = 0; + + virtual bool + Enable (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + Stream *feedback_strm, // Feedback stream for argument errors etc + const Args &categories) = 0;// The categories to enable within this logging stream, if empty, enable default set + + virtual void + ListCategories (Stream *strm) = 0; + +protected: + lldb::LogSP m_log_sp; + +private: + DISALLOW_COPY_AND_ASSIGN (LogChannel); +}; + + +} // namespace lldb_private + +#endif // liblldb_Log_H_ diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h new file mode 100644 index 000000000000..bf99022651a2 --- /dev/null +++ b/lldb/include/lldb/Core/Mangled.h @@ -0,0 +1,485 @@ +//===-- 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: + + //------------------------------------------------------------------ + /// Token type enumerations. + //------------------------------------------------------------------ + enum TokenType + { + eInvalid, ///< Invalid token value (unitialized value) + eNameSpace, ///< The token is a namespace name. + eMethodName, ///< The token is a global or class method name + eType, ///< The token is a language type + eTemplate, ///< The token is a template class + eTemplateBeg, ///< The token that indicates the start of a template parameters + eTemplateEnd, ///< The token that indicates the end of a template parameters + eParamsBeg, ///< The start of a method's parameters (the open parenthesis) + eParamsEnd, ///< The end of a method's parameters (the open parenthesis) + eQualifier, ///< A language qualifier + eError, ///< The token failed to parse + }; + + //------------------------------------------------------------------ + /// Mangled::Token structure + /// + /// As demangled names get tokenized, they get broken up into chunks + /// that have type enumerations (TokenType) and string values. Some of + /// the tokens are scopes (eTemplateBeg, eTemplateEnd, eParamsBeg, + /// eParamsEnd) that can indicate depth and searches can take + /// advantage of these to match using wildcards. + /// + /// For example the mangled string: + /// + /// "_ZNSbIhSt11char_traitsIhESaIhEE5eraseEmm" + /// + /// Demangles to: + /// + /// "std::basic_string, std::allocator >::erase(unsigned long, unsigned long)" + /// + /// And tokenizes to: + /// @li eNameSpace ("std") + /// @li eTemplate ("basic_string") + /// @li eTemplateBeg () + /// @li eType ("unsigned char") + /// @li eNameSpace ("std") + /// @li eTemplate ("char_traits") + /// @li eTemplateBeg () + /// @li eType ("unsigned char") + /// @li eTemplateEnd () + /// @li eNameSpace ("std") + /// @li eTemplate ("allocator") + /// @li eTemplateBeg () + /// @li eType ("unsigned char" + /// @li eTemplateEnd () + /// @li eTemplateEnd () + /// @li eMethodName ("erase") + /// @li eParamsBeg () + /// @li eType ("unsigned long") + /// @li eType ("unsigned long") + /// @li eParamsEnd () + ///------------------------------------------------------------------ + struct Token + { + //-------------------------------------------------------------- + /// Default constructor. + /// + /// Constructs this objet with an invalid token type and an + /// empty string. + //-------------------------------------------------------------- + Token(); + + //-------------------------------------------------------------- + /// Equal to operator. + /// + /// Tests if this object is equal to \a rhs. + /// + /// @param[in] rhs + /// A const Mangled::Token object reference to compare + /// this object to. + /// + /// @return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + //-------------------------------------------------------------- + bool + operator== (const Token& rhs) const; + + //-------------------------------------------------------------- + /// Dump a description of this object to a Stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //-------------------------------------------------------------- + void + Dump (Stream *s) const; + + //-------------------------------------------------------------- + /// Test if this token is a wildcard token. + /// + /// @return + /// Returns \b true if this token is a wildcard, \b false + /// otherwise. + //-------------------------------------------------------------- + bool + IsWildcard() const; + + //-------------------------------------------------------------- + /// Members + //-------------------------------------------------------------- + TokenType type; ///< The type of the token (Mangled::TokenType) + ConstString value; ///< The ConstString value associated with this token + }; + + //------------------------------------------------------------------ + /// A collection of tokens. + /// + /// This class can be instantiated with a demangled names that can + /// be used as a query using the + /// Mangled::TokenList::MatchesQuery(const TokenList&) const + /// function. + //------------------------------------------------------------------ + class TokenList + { + public: + //-------------------------------------------------------------- + /// Construct with a demangled name. + /// + /// If demangled is valid the token list will parse up the + /// demangled string it is given, else the object will + /// initialize an empty token list. + //-------------------------------------------------------------- + TokenList (const char *demangled = NULL); + + //-------------------------------------------------------------- + /// Destructor + //-------------------------------------------------------------- + ~TokenList (); + + //-------------------------------------------------------------- + /// Clear the token list. + //-------------------------------------------------------------- + void + Clear (); + + //-------------------------------------------------------------- + /// Dump a description of this object to a Stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //-------------------------------------------------------------- + void + Dump (Stream *s) const; + + //-------------------------------------------------------------- + /// Find a token by Mangled::TokenType. + /// + /// Find the first token in the list that has \a token_type as + /// its type. + //-------------------------------------------------------------- + const Token* + Find (TokenType token_type) const; + + //-------------------------------------------------------------- + /// Get a token by index. + /// + /// @return + /// The token at index \a idx, or NULL if the index is out + /// of range. + //-------------------------------------------------------------- + const Token* + GetTokenAtIndex (uint32_t idx) const; + + //-------------------------------------------------------------- + /// Given a token list, see if it matches this object's tokens. + /// \a token_list can contain wild card values to enable powerful + /// matching. Matching the std::string::erase(*) example that was + /// tokenized above we could use a token list such as: + /// + /// token name + /// ----------- ---------------------------------------- + /// eNameSpace "std" + /// eTemplate "basic_string" + /// eTemplateBeg + /// eInvalid "*" + /// eTemplateEnd + /// eMethodName "erase" + /// eParamsBeg + /// eInvalid "*" + /// eParamsEnd + /// + /// @return + /// Returns \b true if it \a token_list matches this + /// object's tokens, \b false otherwise. + //-------------------------------------------------------------- + bool + MatchesQuery (const TokenList& token_list) const; + + //-------------------------------------------------------------- + /// Parses \a demangled into tokens. + /// + /// This allows complex comparisons to be done on demangled names. Comparisons can + /// include wildcards at the namespace, method name, template, + /// and template and parameter type levels. + /// + /// Example queries include: + /// "std::basic_string<*>" // Find all std::basic_string variants + /// "std::basic_string<*>::erase(*)" // Find all std::basic_string::erase variants with any number of parameters + /// "*::clear()" // Find all functions with a method name of + /// // "clear" that are in any namespace that + /// // have no parameters + /// "::printf" // Find the printf function in the global namespace + /// "printf" // Ditto + /// "foo::*(int)" // Find all functions in the class or namespace "foo" that take a single integer argument + /// + /// @return + /// The number of tokens that were decoded, or zero if + /// decoding fails. + //-------------------------------------------------------------- + size_t + Parse (const char *demangled); + + //-------------------------------------------------------------- + /// Get the number of tokens in the list. + /// + /// @return + /// The number of tokens in the token list. + //-------------------------------------------------------------- + size_t + Size () const; + + protected: + //-------------------------------------------------------------- + // Member variables. + //-------------------------------------------------------------- + typedef std::vector collection; ///< The collection type for a list of Token objects. + collection m_tokens; ///< The token list. + private: + DISALLOW_COPY_AND_ASSIGN (TokenList); + }; + + //---------------------------------------------------------------------- + /// 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 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 char *name, bool is_mangled); + + //---------------------------------------------------------------------- + /// 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; + + //---------------------------------------------------------------------- + /// Mangled name get accessor. + /// + /// @return + /// A reference to the mangled name string object. + //---------------------------------------------------------------------- + ConstString& + GetMangledName (); + + //---------------------------------------------------------------------- + /// Mangled name get accessor. + /// + /// @return + /// A const reference to the mangled name string object. + //---------------------------------------------------------------------- + const ConstString& + GetMangledName () const; + + //---------------------------------------------------------------------- + /// Best name get accessor. + /// + /// @return + /// A const reference to the the mangled name string object if this + /// object has a valid mangled name, else a const reference to the + /// demangled name is returned. + //---------------------------------------------------------------------- + const ConstString& + GetName () const; + + //---------------------------------------------------------------------- + /// Generate the tokens from the demangled name. + /// + /// @param[out] tokens + /// A token list that will get filled in with the demangled tokens. + /// + /// @return + /// The number of tokens that were parsed and stored in \a tokens. + //---------------------------------------------------------------------- + size_t + GetTokens (Mangled::TokenList &tokens) 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 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. + //---------------------------------------------------------------------- + void + SetValue (const char *name, bool is_mangled); + +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); +Stream& operator << (Stream& s, const Mangled::TokenList& obj); +Stream& operator << (Stream& s, const Mangled::Token& obj); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Mangled_h_ diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h new file mode 100644 index 000000000000..d985ae9495f1 --- /dev/null +++ b/lldb/include/lldb/Core/Module.h @@ -0,0 +1,597 @@ +//===-- Module.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Module_h_ +#define liblldb_Module_h_ + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/TypeList.h" + +//---------------------------------------------------------------------- +/// @class Module Module.h "lldb/Core/Module.h" +/// @brief A class that describes an executable image and its associated +/// object and symbol files. +/// +/// The module is designed to be able to select a single slice of an +/// executable image as it would appear on disk and during program +/// execution. +/// +/// Modules control when and if information is parsed according to which +/// accessors are called. For example the object file (ObjectFile) +/// representation will only be parsed if the object file is requested +/// using the Module::GetObjectFile() is called. The debug symbols +/// will only be parsed if the symbol vendor (SymbolVendor) is +/// requested using the Module::GetSymbolVendor() is called. +/// +/// The module will parse more detailed information as more queries are +/// made. +//---------------------------------------------------------------------- +namespace lldb_private { + +class Module : + public SymbolContextScope +{ +public: + friend class ModuleList; + + enum + { + flagsSearchedForObjParser = (1 << 0), + flagsSearchedForSymVendor = (1 << 1), + flagsParsedUUID = (1 << 2) + }; + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Module (); + + //------------------------------------------------------------------ + /// If you have an instance of Module, get its corresponding shared + /// pointer if it has one in the shared module list. + //------------------------------------------------------------------ + lldb::ModuleSP + GetSP (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext* sc); + + //------------------------------------------------------------------ + /// 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 files 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 functions by name. + /// + /// @param[in] name + /// The name of the function 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[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. + //------------------------------------------------------------------ + uint32_t + FindFunctions (const ConstString &name, bool append, SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @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[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. + //------------------------------------------------------------------ + uint32_t + FindFunctions (const RegularExpression& regex, 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] 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 UINT_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. + //------------------------------------------------------------------ + uint32_t + FindGlobalVariables (const ConstString &name, bool append, uint32_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 UINT_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. + //------------------------------------------------------------------ + uint32_t + FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variable_list); + + //------------------------------------------------------------------ + /// 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 UINT_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. + //------------------------------------------------------------------ +// uint32_t +// FindTypes (const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& type_list); + + //------------------------------------------------------------------ + /// Find types by name. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @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 UINT_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. + //------------------------------------------------------------------ +// uint32_t +// FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, 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. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec & + GetFileSpec () const; + + + const TimeValue & + GetModificationTime () const; + + //------------------------------------------------------------------ + /// Get the number of compile units for this module. + /// + /// @return + /// The number of compile units that the symbol vendor plug-in + /// finds. + //------------------------------------------------------------------ + uint32_t + GetNumCompileUnits(); + + lldb::CompUnitSP + GetCompileUnitAtIndex (uint32_t); + + const ConstString & + GetObjectName() const; + + off_t + GetObjectOffset() const; + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + ObjectFile * + GetObjectFile (); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + SymbolVendor* + GetSymbolVendor(bool can_create = true); + + //------------------------------------------------------------------ + /// 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 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); + +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. + const ArchSpec m_arch; ///< The architecture for this module. + 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). + Flags m_flags; ///< Flags for this module to track what has been parsed already. + 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. + std::auto_ptr m_objfile_ap; ///< A pointer to the object file parser for this module. + std::auto_ptr m_symfile_ap; ///< A pointer to the symbol vendor for this module. + //------------------------------------------------------------------ + /// 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); + +private: + + DISALLOW_COPY_AND_ASSIGN (Module); +}; + +} // namespace lldb_private + +#endif // liblldb_Module_h_ diff --git a/lldb/include/lldb/Core/ModuleChild.h b/lldb/include/lldb/Core/ModuleChild.h new file mode 100644 index 000000000000..0a8475a4425a --- /dev/null +++ b/lldb/include/lldb/Core/ModuleChild.h @@ -0,0 +1,103 @@ +//===-- 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 (Module* module); + + //------------------------------------------------------------------ + /// Copy constructor. + /// + /// @param[in] rhs + /// A const ModuleChild class reference to copy. + //------------------------------------------------------------------ + ModuleChild (const ModuleChild& rhs); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from. + //------------------------------------------------------------------ + virtual + ~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 accessor for the module pointer. + /// + /// @return + /// A pointer to the module that owns this object. + //------------------------------------------------------------------ + Module * + GetModule (); + + //------------------------------------------------------------------ + /// Get const accessor for the module pointer. + /// + /// @return + /// A const pointer to the module that owns the object that + /// inherits from this class. + //------------------------------------------------------------------ + Module * + 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 (Module *module); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Module *m_module; ///< The Module that owns the object that inherits + ///< from this class. +}; + +} // namespace lldb_private + + +#endif // liblldb_ModuleChild_h_ diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h new file mode 100644 index 000000000000..e4959023857a --- /dev/null +++ b/lldb/include/lldb/Core/ModuleList.h @@ -0,0 +1,339 @@ +//===-- 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 "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: + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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 (lldb::ModuleSP &module_sp); + + bool + AppendInNeeded (lldb::ModuleSP &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 (); + + //------------------------------------------------------------------ + /// 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; + + uint32_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 (uint32_t idx); + + //------------------------------------------------------------------ + /// 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 (uint32_t idx) const; + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// Finds all functions that match \a name in all of the modules and + /// returns the results in \a sc_list. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @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, + 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 UINT_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. + //------------------------------------------------------------------ + uint32_t + FindGlobalVariables (const ConstString &name, + bool append, + uint32_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 UINT_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. + //------------------------------------------------------------------ + uint32_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + uint32_t max_matches, + VariableList& variable_list); + + //------------------------------------------------------------------ + /// 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 FileSpec *file_spec_ptr, + const ArchSpec *arch_ptr, + const UUID *uuid_ptr, + const ConstString *object_name, + ModuleList& matching_module_list) const; + + lldb::ModuleSP + FindModule (lldb_private::Module *module_ptr); + + lldb::ModuleSP + FindFirstModuleForFileSpec (const FileSpec &file_spec, + const ConstString *object_name = NULL); + + size_t + FindSymbolsWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list); + + + bool + Remove (lldb::ModuleSP &module_sp); + + bool + ResolveFileAddress (lldb::addr_t vm_addr, + Address& so_addr); + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextForAddress (const Address &,uint32_t,SymbolContext&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForAddress (const Address& so_addr, + uint32_t resolve_scope, + SymbolContext& sc); + + //------------------------------------------------------------------ + /// @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); + + //------------------------------------------------------------------ + /// @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); + + //------------------------------------------------------------------ + /// Gets the size of the module list. + /// + /// @return + /// The number of modules in the module list. + //------------------------------------------------------------------ + size_t + GetSize () const; + + static const lldb::ModuleSP + GetModuleSP (lldb_private::Module *module_ptr); + + static Error + GetSharedModule (const FileSpec& file_spec, + const ArchSpec& arch, + const UUID *uuid_ptr, + const ConstString *object_name, + off_t object_offset, + lldb::ModuleSP &module_sp, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + +protected: + //------------------------------------------------------------------ + // Class typedefs. + //------------------------------------------------------------------ + typedef std::vector collection; ///< The module collection type. + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_modules; ///< The collection of modules. + mutable Mutex m_modules_mutex; + +}; + +} // namespace lldb_private + +#endif // liblldb_ModuleList_h_ diff --git a/lldb/include/lldb/Core/Options.h b/lldb/include/lldb/Core/Options.h new file mode 100644 index 000000000000..acdd3118faeb --- /dev/null +++ b/lldb/include/lldb/Core/Options.h @@ -0,0 +1,296 @@ +//===-- 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/Core/Args.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Options Options.h "lldb/Core/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: +/// \code +/// #include +/// int getopt_long(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 (int 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 () : 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 (); + + 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 + //------------------------------------------------------------------ + struct option * + GetLongOptions (); + + void + OptionSeen (int option_idx); + + 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 +// BuildAliasOptions (OptionArgVector *option_arg_vector, Args args); + + void + OutputFormattedUsageText (Stream &strm, + const char *text, + uint32_t output_max_columns); + + void + GenerateOptionUsage (Stream &strm, + CommandObject *cmd, + const char *program_name = NULL); + + // The following two pure virtual functions must be defined by every class that inherits from + // this class. + + virtual const lldb::OptionDefinition* + GetDefinitions () = 0; + + virtual void + ResetOptionValues () = 0; + + //------------------------------------------------------------------ + /// 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 + //------------------------------------------------------------------ + virtual Error + SetOptionValue (int 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] 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, + lldb_private::CommandInterpreter *interpreter, + lldb_private::StringList &matches); + + //------------------------------------------------------------------ + /// 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] 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[in] interpreter + /// The interpreter that's doing the completing. + /// + /// @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, + CommandInterpreter *interpreter, + StringList &matches); + +protected: + typedef std::set OptionSet; + + std::vector m_getopt_table; + OptionSet m_seen_options; + std::vector m_required_options; + std::vector 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); +}; + +} // namespace lldb_private + +#endif // liblldb_Options_h_ diff --git a/lldb/include/lldb/Core/PluginInterface.h b/lldb/include/lldb/Core/PluginInterface.h new file mode 100644 index 000000000000..2c2823501fc7 --- /dev/null +++ b/lldb/include/lldb/Core/PluginInterface.h @@ -0,0 +1,46 @@ +//===-- 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 const char * + GetPluginName() = 0; + + virtual const char * + GetShortPluginName() = 0; + + virtual uint32_t + GetPluginVersion() = 0; + + virtual void + GetPluginCommandHelp (const char *command, Stream *strm) = 0; + + virtual Error + ExecutePluginCommand (Args &command, Stream *strm) = 0; + + virtual Log * + EnablePluginLogging (Stream *strm, Args &command) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_PluginInterface_h_ diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h new file mode 100644 index 000000000000..fbd12a56dbb1 --- /dev/null +++ b/lldb/include/lldb/Core/PluginManager.h @@ -0,0 +1,186 @@ +//===-- 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" + +namespace lldb_private { + +class PluginManager +{ +public: + //------------------------------------------------------------------ + // ABI + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + ABICreateInstance create_callback); + + static bool + UnregisterPlugin (ABICreateInstance create_callback); + + static ABICreateInstance + GetABICreateCallbackAtIndex (uint32_t idx); + + static ABICreateInstance + GetABICreateCallbackForPluginName (const char *name); + + + //------------------------------------------------------------------ + // Disassembler + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + DisassemblerCreateInstance create_callback); + + static bool + UnregisterPlugin (DisassemblerCreateInstance create_callback); + + static DisassemblerCreateInstance + GetDisassemblerCreateCallbackAtIndex (uint32_t idx); + + static DisassemblerCreateInstance + GetDisassemblerCreateCallbackForPluginName (const char *name); + + + //------------------------------------------------------------------ + // DynamicLoader + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + DynamicLoaderCreateInstance create_callback); + + static bool + UnregisterPlugin (DynamicLoaderCreateInstance create_callback); + + static DynamicLoaderCreateInstance + GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx); + + static DynamicLoaderCreateInstance + GetDynamicLoaderCreateCallbackForPluginName (const char *name); + + + //------------------------------------------------------------------ + // ObjectFile + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + ObjectFileCreateInstance create_callback); + + static bool + UnregisterPlugin (ObjectFileCreateInstance create_callback); + + static ObjectFileCreateInstance + GetObjectFileCreateCallbackAtIndex (uint32_t idx); + + static ObjectFileCreateInstance + GetObjectFileCreateCallbackForPluginName (const char *name); + + + //------------------------------------------------------------------ + // ObjectContainer + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + ObjectContainerCreateInstance create_callback); + + static bool + UnregisterPlugin (ObjectContainerCreateInstance create_callback); + + static ObjectContainerCreateInstance + GetObjectContainerCreateCallbackAtIndex (uint32_t idx); + + static ObjectContainerCreateInstance + GetObjectContainerCreateCallbackForPluginName (const char *name); + + //------------------------------------------------------------------ + // LogChannel + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + LogChannelCreateInstance create_callback); + + static bool + UnregisterPlugin (LogChannelCreateInstance create_callback); + + static LogChannelCreateInstance + GetLogChannelCreateCallbackAtIndex (uint32_t idx); + + static LogChannelCreateInstance + GetLogChannelCreateCallbackForPluginName (const char *name); + + static const char * + GetLogChannelCreateNameAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // Process + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + ProcessCreateInstance create_callback); + + static bool + UnregisterPlugin (ProcessCreateInstance create_callback); + + static ProcessCreateInstance + GetProcessCreateCallbackAtIndex (uint32_t idx); + + static ProcessCreateInstance + GetProcessCreateCallbackForPluginName (const char *name); + + //------------------------------------------------------------------ + // SymbolFile + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + SymbolFileCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolFileCreateInstance create_callback); + + static SymbolFileCreateInstance + GetSymbolFileCreateCallbackAtIndex (uint32_t idx); + + static SymbolFileCreateInstance + GetSymbolFileCreateCallbackForPluginName (const char *name); + + + //------------------------------------------------------------------ + // SymbolVendor + //------------------------------------------------------------------ + static bool + RegisterPlugin (const char *name, + const char *description, + SymbolVendorCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolVendorCreateInstance create_callback); + + static SymbolVendorCreateInstance + GetSymbolVendorCreateCallbackAtIndex (uint32_t idx); + + static SymbolVendorCreateInstance + GetSymbolVendorCreateCallbackForPluginName (const char *name); +}; + + +} // namespace lldb_private + +#endif // liblldb_PluginManager_h_ diff --git a/lldb/include/lldb/Core/RegularExpression.h b/lldb/include/lldb/Core/RegularExpression.h new file mode 100644 index 000000000000..23dabef14434 --- /dev/null +++ b/lldb/include/lldb/Core/RegularExpression.h @@ -0,0 +1,166 @@ +//===-- 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 + +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: + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + RegularExpression (const char* re, int flags = REG_EXTENDED); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Any previosuly compiled regular expression contained in this + /// object will be freed. + //------------------------------------------------------------------ + ~RegularExpression (); + + //------------------------------------------------------------------ + /// 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, int flags = REG_EXTENDED); + + //------------------------------------------------------------------ + /// 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_count + /// The number of regmatch_t objects in \a match_ptr + /// + /// @param[out] match_ptr + /// A pointer to at least \a match_count regmatch_t objects + /// if \a match_count is non-zero. + /// + /// @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, size_t match_count = 0, int execute_flags = 0) const; + + bool + GetMatchAtIndex (const char* s, uint32_t idx, std::string& match_str) 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; + + //------------------------------------------------------------------ + /// 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; + +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 + mutable std::vector m_matches; ///< Where parenthesized subexpressions results are stored +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DBRegex_h_ diff --git a/lldb/include/lldb/Core/STLUtils.h b/lldb/include/lldb/Core/STLUtils.h new file mode 100644 index 000000000000..21b9a0af6c5b --- /dev/null +++ b/lldb/include/lldb/Core/STLUtils.h @@ -0,0 +1,104 @@ +//===-- 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 +#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; + } +}; + +struct StdStringHash +{ + size_t operator()( const std::string& x ) const + { + return __gnu_cxx::hash()( x.c_str() ); + } +}; + + +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/lldb/include/lldb/Core/Scalar.h b/lldb/include/lldb/Core/Scalar.h new file mode 100644 index 000000000000..4207bb6f59e0 --- /dev/null +++ b/lldb/include/lldb/Core/Scalar.h @@ -0,0 +1,302 @@ +//===-- 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(); + + size_t + GetByteSize() const; + + bool + GetData (DataExtractor &data, size_t limit_byte_size = UINT32_MAX) 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); + + 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, uint32_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 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 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); +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/lldb/include/lldb/Core/SearchFilter.h b/lldb/include/lldb/Core/SearchFilter.h new file mode 100644 index 000000000000..5864e85a141b --- /dev/null +++ b/lldb/include/lldb/Core/SearchFilter.h @@ -0,0 +1,326 @@ +//===-- 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/Stream.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/Address.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Core/Module.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 (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 SymbolContext and a SymbolContextScope to see if + /// that SymbolContext passes the filter up to the level in \a scope. + /// + /// @param[in] context + /// The SymbolContext to check against the filter. + /// + /// @param[in] scope + /// The SymbolContextItem indicating what bits of the SymbolContextScope + /// to check against the filter. + /// + /// @return + /// \b true if \a SymbolContext passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + SymbolContextPasses (const SymbolContext &context, + lldb::SymbolContextItem scope); + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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 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 (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 + SymbolContextPasses (const SymbolContext &context, + lldb::SymbolContextItem scope); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpec m_module_spec; +}; + +} // namespace lldb_private + +#endif // liblldb_SearchFilter_h_ diff --git a/lldb/include/lldb/Core/Section.h b/lldb/include/lldb/Core/Section.h new file mode 100644 index 000000000000..358a7489523e --- /dev/null +++ b/lldb/include/lldb/Core/Section.h @@ -0,0 +1,231 @@ +//===-- 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/UserID.h" +#include "lldb/Core/VMRange.h" + +namespace lldb_private { + +class SectionList +{ +public: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + SectionList(); + + virtual + ~SectionList(); + + uint32_t + AddSection (lldb::SectionSP& sect_sp); + + uint32_t + AddUniqueSection (lldb::SectionSP& sect_sp); + + uint32_t + FindSectionIndex (const Section* sect); + + bool + ContainsSection(lldb::user_id_t sect_id) const; + + void + Dump (Stream *s, Process *process, bool show_header) const; + + lldb::SectionSP + FindSectionByName (const ConstString §ion_dstr) const; + +// lldb::SectionSP +// FindSectionByNames (const char *sect, ...) const; + + lldb::SectionSP + FindSectionByID (lldb::user_id_t sect_id) const; + + lldb::SectionSP + GetSharedPointer (const Section *section, bool check_children) const; + + lldb::SectionSP + FindSectionContainingFileAddress (lldb::addr_t addr, uint32_t depth = UINT_MAX) const; + + lldb::SectionSP + FindSectionContainingLinkedFileAddress (lldb::addr_t vm_addr) const; + + bool + GetSectionData (const DataExtractor& module_data, DataExtractor& section_data) const; + + // Get the number of sections in this list only + size_t + GetSize () const; + + // 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, lldb::SectionSP& sect_sp, uint32_t depth = UINT_MAX); + + lldb::SectionSP + GetSectionAtIndex (uint32_t idx) const; + + size_t + Slide (lldb::addr_t slide_amount, bool slide_children); + +protected: + collection m_sections; +}; + + +class Section : + public ModuleChild, + public UserID, + public Flags +{ +public: + Section ( + Section *parent, // NULL for top level sections, non-NULL for child sections + Module* module, + lldb::user_id_t sect_id, + const ConstString &name, + lldb::SectionType sect_type, + lldb::addr_t file_vm_addr, + lldb::addr_t vm_size, + uint64_t file_offset, + uint64_t file_size, + uint32_t flags); + + ~Section (); + + static int + Compare (const Section& a, const Section& b); + + // Get a valid shared pointer to this section object + lldb::SectionSP + GetSharedPointer() const; + + bool + ContainsFileAddress (lldb::addr_t vm_addr) const; + + SectionList& + GetChildren (); + + const SectionList& + GetChildren () const; + + void + Dump (Stream *s, Process *process) const; + + void + DumpName (Stream *s) const; + + lldb::addr_t + GetLoadBaseAddress (Process *process) const; + + bool + ResolveContainedAddress (lldb::addr_t offset, Address &so_addr) const; + + uint64_t + GetFileOffset () const; + + uint64_t + GetFileSize () const; + + lldb::addr_t + GetFileAddress () const; + + lldb::addr_t + GetOffset () const; + + lldb::addr_t + GetByteSize () const; + + void + SetByteSize (lldb::addr_t byte_size); + + size_t + GetSectionDataFromImage (const DataExtractor& image_data, DataExtractor& section_data) const; + + bool + IsFake() const; + + void + SetIsFake(bool fake); + + bool + IsDescendant (const Section *section); + + size_t + MemoryMapSectionDataFromObjectFile (const ObjectFile* file, DataExtractor& section_data) const; + + size_t + ReadSectionDataFromObjectFile (const ObjectFile* file, DataExtractor& section_data) const; + + ConstString& + GetName (); + + const ConstString& + GetName () const; + + bool + Slide (lldb::addr_t slide_amount, bool slide_children); + + void + SetLinkedLocation (const Section *linked_section, uint64_t linked_offset); + + bool + ContainsLinkedFileAddress (lldb::addr_t vm_addr) const; + + const Section * + GetLinkedSection () const; + + uint64_t + GetLinkedOffset () const; + + lldb::addr_t + GetLinkedFileAddress () const; + + lldb::SectionType + GetSectionType () const + { + return m_type; + } + +protected: + + Section * m_parent; // Parent section or NULL if no parent. + ConstString m_name; // Name of this section + lldb::SectionType m_type; // The type 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 + uint64_t m_file_offset; // Object file offset (if any) + uint64_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; // 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. + const Section * m_linked_section; + uint64_t m_linked_offset; +private: + DISALLOW_COPY_AND_ASSIGN (Section); +}; + + +} // namespace lldb_private + +#endif // liblldb_Section_h_ diff --git a/lldb/include/lldb/Core/SourceManager.h b/lldb/include/lldb/Core/SourceManager.h new file mode 100644 index 000000000000..6c6fdfdd602c --- /dev/null +++ b/lldb/include/lldb/Core/SourceManager.h @@ -0,0 +1,120 @@ +//===-- 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/Core/FileSpec.h" + +namespace lldb_private { + +class SourceManager +{ +public: +#ifndef SWIG + class File + { + public: + File (const FileSpec &file_spec); + ~File(); + + size_t + DisplaySourceLines (uint32_t line, + uint32_t context_before, + uint32_t context_after, + Stream *s); + + uint32_t + GetLineOffset (uint32_t line); + + bool + LineIsValid (uint32_t line); + + bool + FileSpecMatches (const FileSpec &file_spec); + + protected: + + bool + CalculateLineOffsets (uint32_t line = UINT32_MAX); + + FileSpec m_file_spec; + lldb::DataBufferSP m_data_sp; + typedef std::vector LineOffsets; + LineOffsets m_offsets; + }; +#endif + + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SourceManager(); + + ~SourceManager(); + + typedef lldb::SharedPtr::Type FileSP; + + FileSP + GetFile (const FileSpec &file_spec); + + size_t + DisplaySourceLines (const FileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + Stream *s); + + size_t + DisplaySourceLinesWithLineNumbers (const FileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s); + + // This variant uses the last file we visited. + size_t + DisplaySourceLinesWithLineNumbersUsingLastFile (uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s); + + size_t + DisplayMoreWithLineNumbers (Stream *s); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from SourceManager can see and modify these + //------------------------------------------------------------------ + typedef std::map FileCache; + FileCache m_file_cache; + FileSP m_last_file_sp; + uint32_t m_last_file_line; + uint32_t m_last_file_context_before; + uint32_t m_last_file_context_after; +private: + //------------------------------------------------------------------ + // For SourceManager only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (SourceManager); +}; + +} // namespace lldb_private + +#endif // liblldb_SourceManager_h_ diff --git a/lldb/include/lldb/Core/State.h b/lldb/include/lldb/Core/State.h new file mode 100644 index 000000000000..2b9372ecd7d1 --- /dev/null +++ b/lldb/include/lldb/Core/State.h @@ -0,0 +1,43 @@ +//===-- 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); + +bool +StateIsRunningState (lldb::StateType state); + +bool +StateIsStoppedState (lldb::StateType state); + +} // namespace lldb_private + +#endif // liblldb_State_h_ diff --git a/lldb/include/lldb/Core/Stream.h b/lldb/include/lldb/Core/Stream.h new file mode 100644 index 000000000000..250bd1f2afd8 --- /dev/null +++ b/lldb/include/lldb/Core/Stream.h @@ -0,0 +1,599 @@ +//===-- 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" + +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 int + Write (const void *src, size_t src_len) = 0; + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + int + 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. + //------------------------------------------------------------------ + int + PrintfAsRawHex8 (const char *format, ...); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + int + PutHex8 (uint8_t uvalue); + + int + PutNHex8 (size_t n, uint8_t uvalue); + + int + PutHex16 (uint16_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + int + PutHex32 (uint32_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + int + PutHex64 (uint64_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + int + PutMaxHex64 (uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + int + PutFloat (float f, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + int + PutDouble (double d, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + int + PutLongDouble (long double ld, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + int + PutPointer (void *ptr); + + int + PutBytesAsRawHex8 (const void *src, + size_t src_len, + lldb::ByteOrder src_byte_order, + lldb::ByteOrder dst_byte_order); + + int + PutRawBytes (const void *s, size_t src_len, + lldb::ByteOrder src_byte_order, + lldb::ByteOrder dst_byte_order); + + int + 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, int 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, int addr_size, const char *prefix = NULL, const char *suffix = NULL); + + //------------------------------------------------------------------ + /// Output a C string to the stream with optional format. + /// + /// Print a C string \a cstr to the stream using the printf format + /// in \a format. + /// + /// @param[in] format + /// The printf style format to use when outputting the C string. + //------------------------------------------------------------------ + int + PutCString (const char *cstr); + + //------------------------------------------------------------------ + /// Output and End of Line character to the stream. + //------------------------------------------------------------------ + int + 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. + //------------------------------------------------------------------ + uint8_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; + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + int + 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. + //------------------------------------------------------------------ + int + Printf (const char *format, ...); + + int + 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 (uint8_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. + //------------------------------------------------------------------ + int + 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. + //------------------------------------------------------------------ + int + PutULEB128 (uint64_t uval); + + static void + UnitTest(Stream *s); + +private: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Flags m_flags; ///< Dump flags. + uint8_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. + + int _PutHex8 (uint8_t uvalue, bool add_prefix); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Stream_h_ + diff --git a/lldb/include/lldb/Core/StreamFile.h b/lldb/include/lldb/Core/StreamFile.h new file mode 100644 index 000000000000..c83f3c2f01c8 --- /dev/null +++ b/lldb/include/lldb/Core/StreamFile.h @@ -0,0 +1,76 @@ +//===-- 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" + +namespace lldb_private { + +class StreamFile : public Stream +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StreamFile (); + + StreamFile (uint32_t flags, uint32_t addr_size, lldb::ByteOrder byte_order, FILE *f); + + StreamFile (FILE *f); + + StreamFile (uint32_t flags, uint32_t addr_size, lldb::ByteOrder byte_order, const char *path, const char *permissions = "w"); + + StreamFile (const char *path, const char *permissions = "w"); + + virtual + ~StreamFile(); + + void + Close (); + + bool + Open (const char *path, const char *permissions = "w"); + + virtual void + Flush (); + + virtual int + Write (const void *s, size_t length); + + FILE * + GetFileHandle (); + + void + SetFileHandle (FILE *file, bool close_file); + + const char * + GetFilePathname (); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StreamFile can see and modify these + //------------------------------------------------------------------ + FILE* m_file; ///< File handle to dump to. + bool m_close_file; + std::string m_path_name; +}; + +} // namespace lldb_private + +#endif // liblldb_StreamFile_h_ diff --git a/lldb/include/lldb/Core/StreamString.h b/lldb/include/lldb/Core/StreamString.h new file mode 100644 index 000000000000..699c7493fe95 --- /dev/null +++ b/lldb/include/lldb/Core/StreamString.h @@ -0,0 +1,61 @@ +//===-- 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 int + Write (const void *s, size_t length); + + void + Clear(); + + void + Dump(FILE *f); + + const char * + GetData () const; + + size_t + GetSize() const; + + std::string & + GetString(); + + const std::string & + GetString() const; + +protected: + std::string m_packet; + +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamString_h_ diff --git a/lldb/include/lldb/Core/StringList.h b/lldb/include/lldb/Core/StringList.h new file mode 100644 index 000000000000..0b29bef54bf6 --- /dev/null +++ b/lldb/include/lldb/Core/StringList.h @@ -0,0 +1,74 @@ +//===-- 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 "lldb/Core/STLUtils.h" + +namespace lldb_private { + +class StringList +{ +public: + + StringList (); + + StringList (const char *str); + + StringList (const char **strv, int strc); + + virtual + ~StringList (); + + 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); + + uint32_t + GetSize (); + + const char * + GetStringAtIndex (size_t idx); + + 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); + +private: + + STLStringArray m_strings; +}; + +} // namespace lldb_private + +#endif // liblldb_StringList_h_ diff --git a/lldb/include/lldb/Core/TTYState.h b/lldb/include/lldb/Core/TTYState.h new file mode 100644 index 000000000000..ad0b62f1b646 --- /dev/null +++ b/lldb/include/lldb/Core/TTYState.h @@ -0,0 +1,202 @@ +//===-- TTYState.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TTYState_h_ +#define liblldb_TTYState_h_ +#if defined(__cplusplus) + +#include +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class TTYState TTYState.h "lldb/Core/TTYState.h" +/// @brief A TTY state managment class. +/// +/// This class can be used to remember the TTY state for a file +/// descriptor and later restore that state as it originally was. +//---------------------------------------------------------------------- +class TTYState +{ +public: + //------------------------------------------------------------------ + /// Default constructor + //------------------------------------------------------------------ + TTYState(); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~TTYState(); + + //------------------------------------------------------------------ + /// 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 TTYState::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; + +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 + //------------------------------------------------------------------ + int m_fd; ///< File descriptor of the TTY. + int m_tflags; ///< Cached tflags information. + int m_ttystate_err; ///< Error value from call to save tflags. + struct termios m_ttystate; ///< Cached ttystate information. + lldb::pid_t m_process_group;///< Cached process group information. + +}; + +//---------------------------------------------------------------------- +/// @class TTYStateSwitcher TTYState.h "lldb/Core/TTYState.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 TTYStateSwitcher +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + TTYStateSwitcher(); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~TTYStateSwitcher(); + + //------------------------------------------------------------------ + /// 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. + TTYState m_ttystates[2]; ///< The array of TTY states that holds saved TTY info. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_TTYState_h_ diff --git a/lldb/include/lldb/Core/ThreadSafeSTLMap.h b/lldb/include/lldb/Core/ThreadSafeSTLMap.h new file mode 100644 index 000000000000..a8921e5a8029 --- /dev/null +++ b/lldb/include/lldb/Core/ThreadSafeSTLMap.h @@ -0,0 +1,170 @@ +//===-- 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() + { + } + + 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/lldb/include/lldb/Core/ThreadSafeValue.h b/lldb/include/lldb/Core/ThreadSafeValue.h new file mode 100644 index 000000000000..42a5a5c6725a --- /dev/null +++ b/lldb/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/lldb/include/lldb/Core/Timer.h b/lldb/include/lldb/Core/Timer.h new file mode 100644 index 000000000000..50c8fdfd386c --- /dev/null +++ b/lldb/include/lldb/Core/Timer.h @@ -0,0 +1,93 @@ +//===-- 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 "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 an 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, ...); + + //-------------------------------------------------------------- + /// Desstructor + //-------------------------------------------------------------- + ~Timer(); + + void + Dump (); + + static void + SetDisplayDepth (uint32_t depth); + + 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); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Timer_h_ diff --git a/lldb/include/lldb/Core/UUID.h b/lldb/include/lldb/Core/UUID.h new file mode 100644 index 000000000000..6e3402b9e74e --- /dev/null +++ b/lldb/include/lldb/Core/UUID.h @@ -0,0 +1,80 @@ +//===-- 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: + typedef uint8_t ValueType[16]; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + UUID (); + UUID (const UUID& rhs); + UUID (const void *uuid_bytes, uint32_t num_uuid_bytes); + UUID (const uuid_t *uuid); + + ~UUID (); + + const UUID& + operator=(const UUID& rhs); + + void + Clear (); + + void + Dump (Stream *s) const; + + const void * + GetBytes() const; + + static size_t + GetByteSize(); + + bool + IsValid () const; + + void + SetBytes (const void *uuid_bytes); + + char * + GetAsCString (char *dst, size_t dst_len) const; + + size_t + SetfromCString (const char *c_str); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from UUID can see and modify these + //------------------------------------------------------------------ + 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/lldb/include/lldb/Core/UniqueCStringMap.h b/lldb/include/lldb/Core/UniqueCStringMap.h new file mode 100644 index 000000000000..34b91f30a6d7 --- /dev/null +++ b/lldb/include/lldb/Core/UniqueCStringMap.h @@ -0,0 +1,232 @@ +//===-- 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 + +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 entry by index. + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned pointer. + //------------------------------------------------------------------ + const T * + GetValueAtIndex (uint32_t idx) const + { + if (idx < m_map.size()) + return &m_map[idx].value; + return NULL; + } + + const char * + GetCStringAtIndex (uint32_t idx) const + { + if (idx < m_map.size()) + return m_map[idx].cstring; + return NULL; + } + + //------------------------------------------------------------------ + // 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 char *unique_cstr, const Entry *entry_ptr) const + { + const Entry *first_entry = m_map.data(); + 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 == unique_cstr) + return next_entry; + } + return NULL; + } + + //------------------------------------------------------------------ + // Get the total number of entries in this map. + //------------------------------------------------------------------ + size_t + GetSize () + { + 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()); + } + +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/lldb/include/lldb/Core/UserID.h b/lldb/include/lldb/Core/UserID.h new file mode 100644 index 000000000000..a12884afe6d7 --- /dev/null +++ b/lldb/include/lldb/Core/UserID.h @@ -0,0 +1,123 @@ +//===-- 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); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~UserID (); + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Get accessor for the user ID. + /// + /// @return + /// The user ID. + //------------------------------------------------------------------ + lldb::user_id_t + GetID () const; + + //------------------------------------------------------------------ + /// Set accessor for the user ID. + /// + /// @param[in] uid + /// The new user ID. + //------------------------------------------------------------------ + void + SetID (lldb::user_id_t 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); + + //-------------------------------------------------------------- + /// Unary predicate function object callback. + //-------------------------------------------------------------- + bool + operator () (const UserID& rhs) const; + + 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. +}; + +//-------------------------------------------------------------- +/// Stream the UserID object to a Stream. +//-------------------------------------------------------------- +bool operator== (const UserID& lhs, const UserID& rhs); +bool operator!= (const UserID& lhs, const UserID& rhs); +Stream& operator << (Stream& strm, const UserID& uid); + +} // namespace lldb_private + +#endif // liblldb_UserID_h_ diff --git a/lldb/include/lldb/Core/VMRange.h b/lldb/include/lldb/Core/VMRange.h new file mode 100644 index 000000000000..b97059f25778 --- /dev/null +++ b/lldb/include/lldb/Core/VMRange.h @@ -0,0 +1,165 @@ +//===-- 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() + { + } + + // 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 GetByteSize() > 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) 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); + +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/lldb/include/lldb/Core/Value.h b/lldb/include/lldb/Core/Value.h new file mode 100644 index 000000000000..9aaf44abc722 --- /dev/null +++ b/lldb/include/lldb/Core/Value.h @@ -0,0 +1,172 @@ +//===-- 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 +// 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" + +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 + 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 + eContextTypeOpaqueClangQualType,// void * (an opaque clang::QualType * that can be fed to "static QualType QualType::getFromOpaquePtr(void *)") + eContextTypeDCRegisterInfo, // lldb::RegisterInfo * + eContextTypeDCType, // Type * + eContextTypeDCVariable, // Variable * + eContextTypeValue // Value * (making this a proxy value. Used when putting locals on the DWARF expression parser stack) + }; + + Value(); + Value(const Scalar& scalar); + Value(int v); + Value(unsigned int v); + Value(long v); + Value(unsigned long v); + Value(long long v); + Value(unsigned long long v); + Value(float v); + Value(double v); + Value(long double v); + Value(const uint8_t *bytes, int len); + Value(const Value &v); + + Value * + CreateProxy(); + + Value * + GetProxyTarget(); + + void * + GetOpaqueClangQualType(); + + ValueType + GetValueType() const; + + lldb::AddressType + GetValueAddressType () const; + + ContextType + GetContextType() const; + + void + SetValueType (ValueType value_type); + + void + ClearContext (); + + void + SetContext (ContextType context_type, void *p); + + lldb::RegisterInfo * + GetRegisterInfo(); + + Type * + GetType(); + + Scalar & + ResolveValue (ExecutionContext *exe_ctx, clang::ASTContext *ast_context); + + Scalar & + GetScalar(); + + void + ResizeData(int len); + + bool + ValueOf(ExecutionContext *exe_ctx, clang::ASTContext *ast_context); + + Variable * + GetVariable(); + + void + Dump (Stream* strm); + + lldb::Format + GetValueDefaultFormat (); + + void * + GetValueOpaqueClangQualType (); + + size_t + GetValueByteSize (clang::ASTContext *ast_context, Error *error_ptr); + + Error + GetValueAsData (ExecutionContext *exe_ctx, clang::ASTContext *ast_context, DataExtractor &data, uint32_t data_offset); + + static const char * + GetValueTypeAsCString (ValueType context_type); + + static const char * + GetContextTypeAsCString (ContextType context_type); + +protected: + Scalar m_value; + ValueType m_value_type; + void * m_context; + ContextType m_context_type; + DataBufferHeap m_data_buffer; +}; + +class ValueList +{ +public: + ValueList () {} + 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/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h new file mode 100644 index 000000000000..75ae31bdffb9 --- /dev/null +++ b/lldb/include/lldb/Core/ValueObject.h @@ -0,0 +1,230 @@ +//===-- 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 +// 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/ExecutionContextScope.h" + +namespace lldb_private { + +class ValueObject : public UserID +{ +public: + friend class ValueObjectList; + + virtual ~ValueObject(); + + //------------------------------------------------------------------ + // Sublasses must implement the functions below. + //------------------------------------------------------------------ + virtual size_t + GetByteSize() = 0; + + virtual clang::ASTContext * + GetClangAST () = 0; + + virtual void * + GetOpaqueClangQualType () = 0; + + virtual lldb::ValueType + GetValueType() const = 0; + +protected: + // Should only be called by ValueObject::GetNumChildren() + virtual uint32_t + CalculateNumChildren() = 0; + +public: + virtual ConstString + GetTypeName() = 0; + + virtual void + UpdateValue (ExecutionContextScope *exe_scope) = 0; + + //------------------------------------------------------------------ + // Sublasses can implement the functions below if they need to. + //------------------------------------------------------------------ +protected: + // Should only be called by ValueObject::GetChildAtIndex() + virtual lldb::ValueObjectSP + CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index); + +public: + + virtual bool + IsPointerType (); + + virtual bool + IsPointerOrReferenceType (); + + virtual bool + IsInScope (StackFrame *frame) + { + return true; + } + + virtual off_t + GetByteOffset() + { + return 0; + } + + virtual uint32_t + GetBitfieldBitSize() + { + return 0; + } + + virtual uint32_t + GetBitfieldBitOffset() + { + return 0; + } + + virtual const char * + GetValueAsCString (ExecutionContextScope *exe_scope); + + virtual bool + SetValueFromCString (ExecutionContextScope *exe_scope, const char *value_str); + + //------------------------------------------------------------------ + // The functions below should NOT be modified by sublasses + //------------------------------------------------------------------ + const Error & + GetError() const; + + const ConstString & + GetName() const; + + lldb::ValueObjectSP + GetChildAtIndex (uint32_t idx, bool can_create); + + lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + uint32_t + GetIndexOfChildWithName (const ConstString &name); + + uint32_t + GetNumChildren (); + + const Value & + GetValue() const; + + Value & + GetValue(); + + const char * + GetLocationAsCString (ExecutionContextScope *exe_scope); + + const char * + GetSummaryAsCString (ExecutionContextScope *exe_scope); + + lldb::user_id_t + GetUpdateID() const; + + bool + GetValueIsValid (); + + bool + GetValueDidChange () const; + + bool + UpdateValueIfNeeded (ExecutionContextScope *exe_scope); + + const DataExtractor & + GetDataExtractor () const; + + DataExtractor & + GetDataExtractor (); + + bool + Write (); + + void + AddSyntheticChild (const ConstString &key, + lldb::ValueObjectSP& valobj_sp); + + lldb::ValueObjectSP + GetSyntheticChild (const ConstString &key) const; + + lldb::ValueObjectSP + GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create); + +protected: + enum { + eValueChanged = (1 << 0), + eNumChildrenHasBeenSet = (1 << 1), + eValueIsValid = (1 << 2) + }; + //------------------------------------------------------------------ + // Classes that inherit from ValueObject can see and modify these + //------------------------------------------------------------------ + lldb::user_id_t m_update_id; // An integer that specifies the update number for this value in + // this value object list. If this value object is asked to update itself + // it will first check if the update ID match the value object + // list update number. If the update numbers match, no update is + // needed, if it does not match, this value object should update its + // the next time it is asked. + 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. + Flags m_flags; // A boolean that indicates this value has changed + std::string m_value_str; // Cached value string that will get cleared if/when the value is updated. + 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::vector m_children; + std::map m_synthetic_children; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ValueObject (); + + void + SetName (const char *name); + + void + SetName (const ConstString &name); + + void + SetNumChildren (uint32_t num_children); + + void + SetValueDidChange (bool value_changed); + + void + SetValueIsValid (bool valid); + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObject); + +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObject_h_ diff --git a/lldb/include/lldb/Core/ValueObjectChild.h b/lldb/include/lldb/Core/ValueObjectChild.h new file mode 100644 index 000000000000..b5f842cfba99 --- /dev/null +++ b/lldb/include/lldb/Core/ValueObjectChild.h @@ -0,0 +1,95 @@ +//===-- 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 +#include "clang/AST/Type.h" +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A child of another ValueObject. +//---------------------------------------------------------------------- +class ValueObjectChild : public ValueObject +{ +public: + ValueObjectChild (ValueObject *parent, + clang::ASTContext *clang_ast, + void *clang_type, + const ConstString &name, + uint32_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset); + + + virtual ~ValueObjectChild(); + + virtual size_t + GetByteSize(); + + virtual off_t + GetByteOffset(); + + virtual uint32_t + GetBitfieldBitSize(); + + virtual uint32_t + GetBitfieldBitOffset(); + + virtual clang::ASTContext * + GetClangAST (); + + virtual void * + GetOpaqueClangQualType (); + + virtual lldb::ValueType + GetValueType() const; + + virtual uint32_t + CalculateNumChildren(); + + virtual ConstString + GetTypeName(); + + virtual void + UpdateValue (ExecutionContextScope *exe_scope); + + virtual bool + IsInScope (StackFrame *frame); + +protected: + ValueObject* m_parent; // The parent value object of which this is a child of. + clang::ASTContext *m_clang_ast; // The clang AST that the clang type comes from + void *m_clang_type; // The type of the child in question within the parent (m_parent_sp) + ConstString m_type_name; + uint32_t m_byte_size; + int32_t m_byte_offset; + uint32_t m_bitfield_bit_size; + uint32_t m_bitfield_bit_offset; + + uint32_t + GetByteOffset() const; +// +// void +// ReadValueFromMemory (ValueObject* parent, lldb::addr_t address); + +private: + DISALLOW_COPY_AND_ASSIGN (ValueObjectChild); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectChild_h_ diff --git a/lldb/include/lldb/Core/ValueObjectList.h b/lldb/include/lldb/Core/ValueObjectList.h new file mode 100644 index 000000000000..b312790acab3 --- /dev/null +++ b/lldb/include/lldb/Core/ValueObjectList.h @@ -0,0 +1,74 @@ +//===-- 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); + + lldb::ValueObjectSP + FindValueObjectByPointer (ValueObject *valobj); + + uint32_t + GetSize() const; + + lldb::ValueObjectSP + GetValueObjectAtIndex (uint32_t); + + lldb::ValueObjectSP + FindValueObjectByValueName (const char *name); + + lldb::ValueObjectSP + FindValueObjectByUID (lldb::user_id_t uid); + +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/lldb/include/lldb/Core/ValueObjectRegister.h b/lldb/include/lldb/Core/ValueObjectRegister.h new file mode 100644 index 000000000000..fe3ac480caa1 --- /dev/null +++ b/lldb/include/lldb/Core/ValueObjectRegister.h @@ -0,0 +1,168 @@ +//===-- 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/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that contains a root variable that may or may not +// have children. +//---------------------------------------------------------------------- +class ValueObjectRegisterContext : public ValueObject +{ +public: + ValueObjectRegisterContext (RegisterContext *reg_ctx); + + virtual + ~ValueObjectRegisterContext(); + + virtual size_t + GetByteSize(); + + virtual clang::ASTContext * + GetClangAST (); + + virtual void * + GetOpaqueClangQualType (); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegisterSet; + } + + virtual ConstString + GetTypeName(); + + virtual uint32_t + CalculateNumChildren(); + + virtual void + UpdateValue (ExecutionContextScope *exe_scope); + + virtual lldb::ValueObjectSP + CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index); + +protected: + RegisterContext *m_reg_ctx; + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegisterContext); +}; + +class ValueObjectRegisterSet : public ValueObject +{ +public: + ValueObjectRegisterSet (RegisterContext *reg_ctx, uint32_t set_idx); + + virtual + ~ValueObjectRegisterSet(); + + virtual size_t + GetByteSize(); + + virtual clang::ASTContext * + GetClangAST (); + + virtual void * + GetOpaqueClangQualType (); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegisterSet; + } + + virtual ConstString + GetTypeName(); + + virtual uint32_t + CalculateNumChildren(); + + virtual void + UpdateValue (ExecutionContextScope *exe_scope); + + virtual lldb::ValueObjectSP + CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index); + +protected: + + RegisterContext *m_reg_ctx; + const lldb::RegisterSet *m_reg_set; + uint32_t m_reg_set_idx; + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegisterSet); +}; + +class ValueObjectRegister : public ValueObject +{ +public: + ValueObjectRegister (RegisterContext *reg_ctx, uint32_t reg_num); + + virtual + ~ValueObjectRegister(); + + virtual size_t + GetByteSize(); + + virtual clang::ASTContext * + GetClangAST (); + + virtual void * + GetOpaqueClangQualType (); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegister; + } + + virtual ConstString + GetTypeName(); + + virtual uint32_t + CalculateNumChildren(); + + virtual void + UpdateValue (ExecutionContextScope *exe_scope); + +protected: + + RegisterContext *m_reg_ctx; + const lldb::RegisterInfo *m_reg_info; + uint32_t m_reg_num; + ConstString m_type_name; + void *m_clang_type; + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegister); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectRegister_h_ diff --git a/lldb/include/lldb/Core/ValueObjectVariable.h b/lldb/include/lldb/Core/ValueObjectVariable.h new file mode 100644 index 000000000000..80e0d05272ef --- /dev/null +++ b/lldb/include/lldb/Core/ValueObjectVariable.h @@ -0,0 +1,70 @@ +//===-- 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: + ValueObjectVariable (lldb::VariableSP &var_sp); + + virtual + ~ValueObjectVariable(); + + virtual size_t + GetByteSize(); + + virtual clang::ASTContext * + GetClangAST (); + + virtual void * + GetOpaqueClangQualType (); + + virtual ConstString + GetTypeName(); + + virtual uint32_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual void + UpdateValue (ExecutionContextScope *exe_scope); + + virtual bool + IsInScope (StackFrame *frame); + +protected: + + lldb::VariableSP m_variable_sp; ///< The variable that this value object is based upon + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectVariable); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectVariable_h_ diff --git a/lldb/include/lldb/Core/dwarf.h b/lldb/include/lldb/Core/dwarf.h new file mode 100644 index 000000000000..2b566dfd9d9d --- /dev/null +++ b/lldb/include/lldb/Core/dwarf.h @@ -0,0 +1,589 @@ +//===-- 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 + +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_ADDRESS (~(dw_addr_t)0) +#define DW_INVALID_OFFSET (~(dw_offset_t)0) +#define DW_INVALID_INDEX 0xFFFFFFFFul + + +/* [7.5.4] Figure 16 "Tag Encodings" (pp. 125-127) in DWARFv3 draft 8 */ + +#define DW_TAG_array_type 0x1 +#define DW_TAG_class_type 0x2 +#define DW_TAG_entry_point 0x3 +#define DW_TAG_enumeration_type 0x4 +#define DW_TAG_formal_parameter 0x5 +#define DW_TAG_imported_declaration 0x8 +#define DW_TAG_label 0xA +#define DW_TAG_lexical_block 0xB +#define DW_TAG_member 0xD +#define DW_TAG_pointer_type 0xF +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_string_type 0x12 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_variant 0x19 +#define DW_TAG_common_block 0x1A +#define DW_TAG_common_inclusion 0x1B +#define DW_TAG_inheritance 0x1C +#define DW_TAG_inlined_subroutine 0x1D +#define DW_TAG_module 0x1E +#define DW_TAG_ptr_to_member_type 0x1F +#define DW_TAG_set_type 0x20 +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_with_stmt 0x22 +#define DW_TAG_access_declaration 0x23 +#define DW_TAG_base_type 0x24 +#define DW_TAG_catch_block 0x25 +#define DW_TAG_const_type 0x26 +#define DW_TAG_constant 0x27 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_file_type 0x29 +#define DW_TAG_friend 0x2A +#define DW_TAG_namelist 0x2B +#define DW_TAG_namelist_item 0x2C +#define DW_TAG_packed_type 0x2D +#define DW_TAG_subprogram 0x2E +#define DW_TAG_template_type_parameter 0x2F +#define DW_TAG_template_value_parameter 0x30 +#define DW_TAG_thrown_type 0x31 +#define DW_TAG_try_block 0x32 +#define DW_TAG_variant_part 0x33 +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 +#define DW_TAG_dwarf_procedure 0x36 +#define DW_TAG_restrict_type 0x37 +#define DW_TAG_interface_type 0x38 +#define DW_TAG_namespace 0x39 +#define DW_TAG_imported_module 0x3A +#define DW_TAG_unspecified_type 0x3B +#define DW_TAG_partial_unit 0x3C +#define DW_TAG_imported_unit 0x3D +#define DW_TAG_condition 0x3F +#define DW_TAG_shared_type 0x40 +#define DW_TAG_lo_user 0x4080 +#define DW_TAG_hi_user 0xFFFF + +/* [7.5.4] Figure 17 "Child determination encodings" (p. 128) in DWARFv3 draft 8 */ + +#define DW_CHILDREN_no 0x0 +#define DW_CHILDREN_yes 0x1 + +/* [7.5.4] Figure 18 "Attribute encodings" (pp. 129-132) in DWARFv3 draft 8 */ + +#define DW_AT_sibling 0x1 +#define DW_AT_location 0x2 +#define DW_AT_name 0x3 +#define DW_AT_ordering 0x9 +#define DW_AT_byte_size 0xB +#define DW_AT_bit_offset 0xC +#define DW_AT_bit_size 0xD +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_discr 0x15 +#define DW_AT_discr_value 0x16 +#define DW_AT_visibility 0x17 +#define DW_AT_import 0x18 +#define DW_AT_string_length 0x19 +#define DW_AT_common_reference 0x1A +#define DW_AT_comp_dir 0x1B +#define DW_AT_const_value 0x1C +#define DW_AT_containing_type 0x1D +#define DW_AT_default_value 0x1E +#define DW_AT_inline 0x20 +#define DW_AT_is_optional 0x21 +#define DW_AT_lower_bound 0x22 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_return_addr 0x2A +#define DW_AT_start_scope 0x2C +#define DW_AT_bit_stride 0x2E +#define DW_AT_upper_bound 0x2F +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_address_class 0x33 +#define DW_AT_artificial 0x34 +#define DW_AT_base_types 0x35 +#define DW_AT_calling_convention 0x36 +#define DW_AT_count 0x37 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_column 0x39 +#define DW_AT_decl_file 0x3A +#define DW_AT_decl_line 0x3B +#define DW_AT_declaration 0x3C +#define DW_AT_discr_list 0x3D +#define DW_AT_encoding 0x3E +#define DW_AT_external 0x3F +#define DW_AT_frame_base 0x40 +#define DW_AT_friend 0x41 +#define DW_AT_identifier_case 0x42 +#define DW_AT_macro_info 0x43 +#define DW_AT_namelist_item 0x44 +#define DW_AT_priority 0x45 +#define DW_AT_segment 0x46 +#define DW_AT_specification 0x47 +#define DW_AT_static_link 0x48 +#define DW_AT_type 0x49 +#define DW_AT_use_location 0x4A +#define DW_AT_variable_parameter 0x4B +#define DW_AT_virtuality 0x4C +#define DW_AT_vtable_elem_location 0x4D +#define DW_AT_allocated 0x4E +#define DW_AT_associated 0x4F +#define DW_AT_data_location 0x50 +#define DW_AT_byte_stride 0x51 +#define DW_AT_entry_pc 0x52 +#define DW_AT_use_UTF8 0x53 +#define DW_AT_extension 0x54 +#define DW_AT_ranges 0x55 +#define DW_AT_trampoline 0x56 +#define DW_AT_call_column 0x57 +#define DW_AT_call_file 0x58 +#define DW_AT_call_line 0x59 +#define DW_AT_description 0x5A +#define DW_AT_binary_scale 0x5B +#define DW_AT_decimal_scale 0x5C +#define DW_AT_small 0x5D +#define DW_AT_decimal_sign 0x5E +#define DW_AT_digit_count 0x5F +#define DW_AT_picture_string 0x60 +#define DW_AT_mutable 0x61 +#define DW_AT_threads_scaled 0x62 +#define DW_AT_explicit 0x63 +#define DW_AT_object_pointer 0x64 +#define DW_AT_endianity 0x65 +#define DW_AT_elemental 0x66 +#define DW_AT_pure 0x67 +#define DW_AT_recursive 0x68 +#define DW_AT_lo_user 0x2000 +#define DW_AT_hi_user 0x3FFF +#define DW_AT_MIPS_fde 0x2001 +#define DW_AT_MIPS_loop_begin 0x2002 +#define DW_AT_MIPS_tail_loop_begin 0x2003 +#define DW_AT_MIPS_epilog_begin 0x2004 +#define DW_AT_MIPS_loop_unroll_factor 0x2005 +#define DW_AT_MIPS_software_pipeline_depth 0x2006 +#define DW_AT_MIPS_linkage_name 0x2007 +#define DW_AT_MIPS_stride 0x2008 +#define DW_AT_MIPS_abstract_name 0x2009 +#define DW_AT_MIPS_clone_origin 0x200A +#define DW_AT_MIPS_has_inlines 0x200B +/* GNU extensions. */ +#define DW_AT_sf_names 0x2101 +#define DW_AT_src_info 0x2102 +#define DW_AT_mac_info 0x2103 +#define DW_AT_src_coords 0x2104 +#define DW_AT_body_begin 0x2105 +#define DW_AT_body_end 0x2106 +#define DW_AT_GNU_vector 0x2107 + +#define DW_AT_APPLE_repository_file 0x2501 +#define DW_AT_APPLE_repository_type 0x2502 +#define DW_AT_APPLE_repository_name 0x2503 +#define DW_AT_APPLE_repository_specification 0x2504 +#define DW_AT_APPLE_repository_import 0x2505 +#define DW_AT_APPLE_repository_abstract_origin 0x2506 +#define DW_AT_APPLE_optimized 0x3FE1 +#define DW_AT_APPLE_flags 0x3FE2 +#define DW_AT_APPLE_isa 0x3FE3 +#define DW_AT_APPLE_block 0x3FE4 + +/* [7.5.4] Figure 19 "Attribute form encodings" (pp. 133-134) in DWARFv3 draft 8 */ + +#define DW_FORM_addr 0x1 +#define DW_FORM_block2 0x3 +#define DW_FORM_block4 0x4 +#define DW_FORM_data2 0x5 +#define DW_FORM_data4 0x6 +#define DW_FORM_data8 0x7 +#define DW_FORM_string 0x8 +#define DW_FORM_block 0x9 +#define DW_FORM_block1 0xA +#define DW_FORM_data1 0xB +#define DW_FORM_flag 0xC +#define DW_FORM_sdata 0xD +#define DW_FORM_strp 0xE +#define DW_FORM_udata 0xF +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref1 0x11 +#define DW_FORM_ref2 0x12 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref8 0x14 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 // cf section 7.5.3, "Abbreviations Tables", p. 119 DWARFv3 draft 8 +#define DW_FORM_APPLE_db_str 0x50 // read same as udata, but refers to string in repository + +/* [7.7.1] Figure 22 "DWARF operation encodings" (pp. 136-139) in DWARFv3 draft 8 */ + +#define DW_OP_addr 0x3 // constant address (size target specific) +#define DW_OP_deref 0x6 +#define DW_OP_const1u 0x8 // 1-byte constant +#define DW_OP_const1s 0x9 // 1-byte constant +#define DW_OP_const2u 0xA // 2-byte constant +#define DW_OP_const2s 0xB // 2-byte constant +#define DW_OP_const4u 0xC // 4-byte constant +#define DW_OP_const4s 0xD // 4-byte constant +#define DW_OP_const8u 0xE // 8-byte constant +#define DW_OP_const8s 0xF // 8-byte constant +#define DW_OP_constu 0x10 // ULEB128 constant +#define DW_OP_consts 0x11 // SLEB128 constant +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 // 1-byte stack index +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1A +#define DW_OP_div 0x1B +#define DW_OP_minus 0x1C +#define DW_OP_mod 0x1D +#define DW_OP_mul 0x1E +#define DW_OP_neg 0x1F +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 // ULEB128 addend +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_skip 0x2F // signed 2-byte constant +#define DW_OP_bra 0x28 // signed 2-byte constant +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2A +#define DW_OP_gt 0x2B +#define DW_OP_le 0x2C +#define DW_OP_lt 0x2D +#define DW_OP_ne 0x2E +#define DW_OP_lit0 0x30 // Literal 0 +#define DW_OP_lit1 0x31 // Literal 1 +#define DW_OP_lit2 0x32 // Literal 2 +#define DW_OP_lit3 0x33 // Literal 3 +#define DW_OP_lit4 0x34 // Literal 4 +#define DW_OP_lit5 0x35 // Literal 5 +#define DW_OP_lit6 0x36 // Literal 6 +#define DW_OP_lit7 0x37 // Literal 7 +#define DW_OP_lit8 0x38 // Literal 8 +#define DW_OP_lit9 0x39 // Literal 9 +#define DW_OP_lit10 0x3A // Literal 10 +#define DW_OP_lit11 0x3B // Literal 11 +#define DW_OP_lit12 0x3C // Literal 12 +#define DW_OP_lit13 0x3D // Literal 13 +#define DW_OP_lit14 0x3E // Literal 14 +#define DW_OP_lit15 0x3F // Literal 15 +#define DW_OP_lit16 0x40 // Literal 16 +#define DW_OP_lit17 0x41 // Literal 17 +#define DW_OP_lit18 0x42 // Literal 18 +#define DW_OP_lit19 0x43 // Literal 19 +#define DW_OP_lit20 0x44 // Literal 20 +#define DW_OP_lit21 0x45 // Literal 21 +#define DW_OP_lit22 0x46 // Literal 22 +#define DW_OP_lit23 0x47 // Literal 23 +#define DW_OP_lit24 0x48 // Literal 24 +#define DW_OP_lit25 0x49 // Literal 25 +#define DW_OP_lit26 0x4A // Literal 26 +#define DW_OP_lit27 0x4B // Literal 27 +#define DW_OP_lit28 0x4C // Literal 28 +#define DW_OP_lit29 0x4D // Literal 29 +#define DW_OP_lit30 0x4E // Literal 30 +#define DW_OP_lit31 0x4F // Literal 31 +#define DW_OP_reg0 0x50 // Contents of reg0 +#define DW_OP_reg1 0x51 // Contents of reg1 +#define DW_OP_reg2 0x52 // Contents of reg2 +#define DW_OP_reg3 0x53 // Contents of reg3 +#define DW_OP_reg4 0x54 // Contents of reg4 +#define DW_OP_reg5 0x55 // Contents of reg5 +#define DW_OP_reg6 0x56 // Contents of reg6 +#define DW_OP_reg7 0x57 // Contents of reg7 +#define DW_OP_reg8 0x58 // Contents of reg8 +#define DW_OP_reg9 0x59 // Contents of reg9 +#define DW_OP_reg10 0x5A // Contents of reg10 +#define DW_OP_reg11 0x5B // Contents of reg11 +#define DW_OP_reg12 0x5C // Contents of reg12 +#define DW_OP_reg13 0x5D // Contents of reg13 +#define DW_OP_reg14 0x5E // Contents of reg14 +#define DW_OP_reg15 0x5F // Contents of reg15 +#define DW_OP_reg16 0x60 // Contents of reg16 +#define DW_OP_reg17 0x61 // Contents of reg17 +#define DW_OP_reg18 0x62 // Contents of reg18 +#define DW_OP_reg19 0x63 // Contents of reg19 +#define DW_OP_reg20 0x64 // Contents of reg20 +#define DW_OP_reg21 0x65 // Contents of reg21 +#define DW_OP_reg22 0x66 // Contents of reg22 +#define DW_OP_reg23 0x67 // Contents of reg23 +#define DW_OP_reg24 0x68 // Contents of reg24 +#define DW_OP_reg25 0x69 // Contents of reg25 +#define DW_OP_reg26 0x6A // Contents of reg26 +#define DW_OP_reg27 0x6B // Contents of reg27 +#define DW_OP_reg28 0x6C // Contents of reg28 +#define DW_OP_reg29 0x6D // Contents of reg29 +#define DW_OP_reg30 0x6E // Contents of reg30 +#define DW_OP_reg31 0x6F // Contents of reg31 +#define DW_OP_breg0 0x70 // base register 0 + SLEB128 offset +#define DW_OP_breg1 0x71 // base register 1 + SLEB128 offset +#define DW_OP_breg2 0x72 // base register 2 + SLEB128 offset +#define DW_OP_breg3 0x73 // base register 3 + SLEB128 offset +#define DW_OP_breg4 0x74 // base register 4 + SLEB128 offset +#define DW_OP_breg5 0x75 // base register 5 + SLEB128 offset +#define DW_OP_breg6 0x76 // base register 6 + SLEB128 offset +#define DW_OP_breg7 0x77 // base register 7 + SLEB128 offset +#define DW_OP_breg8 0x78 // base register 8 + SLEB128 offset +#define DW_OP_breg9 0x79 // base register 9 + SLEB128 offset +#define DW_OP_breg10 0x7A // base register 10 + SLEB128 offset +#define DW_OP_breg11 0x7B // base register 11 + SLEB128 offset +#define DW_OP_breg12 0x7C // base register 12 + SLEB128 offset +#define DW_OP_breg13 0x7D // base register 13 + SLEB128 offset +#define DW_OP_breg14 0x7E // base register 14 + SLEB128 offset +#define DW_OP_breg15 0x7F // base register 15 + SLEB128 offset +#define DW_OP_breg16 0x80 // base register 16 + SLEB128 offset +#define DW_OP_breg17 0x81 // base register 17 + SLEB128 offset +#define DW_OP_breg18 0x82 // base register 18 + SLEB128 offset +#define DW_OP_breg19 0x83 // base register 19 + SLEB128 offset +#define DW_OP_breg20 0x84 // base register 20 + SLEB128 offset +#define DW_OP_breg21 0x85 // base register 21 + SLEB128 offset +#define DW_OP_breg22 0x86 // base register 22 + SLEB128 offset +#define DW_OP_breg23 0x87 // base register 23 + SLEB128 offset +#define DW_OP_breg24 0x88 // base register 24 + SLEB128 offset +#define DW_OP_breg25 0x89 // base register 25 + SLEB128 offset +#define DW_OP_breg26 0x8A // base register 26 + SLEB128 offset +#define DW_OP_breg27 0x8B // base register 27 + SLEB128 offset +#define DW_OP_breg28 0x8C // base register 28 + SLEB128 offset +#define DW_OP_breg29 0x8D // base register 29 + SLEB128 offset +#define DW_OP_breg30 0x8E // base register 30 + SLEB128 offset +#define DW_OP_breg31 0x8F // base register 31 + SLEB128 offset +#define DW_OP_regx 0x90 // ULEB128 register +#define DW_OP_fbreg 0x91 // SLEB128 offset +#define DW_OP_bregx 0x92 // ULEB128 register followed by SLEB128 offset +#define DW_OP_piece 0x93 // ULEB128 size of piece addressed +#define DW_OP_deref_size 0x94 // 1-byte size of data retrieved +#define DW_OP_xderef_size 0x95 // 1-byte size of data retrieved +#define DW_OP_nop 0x96 +#define DW_OP_push_object_address 0x97 +#define DW_OP_call2 0x98 // 2-byte offset of DIE +#define DW_OP_call4 0x99 // 4-byte offset of DIE +#define DW_OP_call_ref 0x9A // 4- or 8-byte offset of DIE +#define DW_OP_lo_user 0xE0 +#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 +#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) +#define DW_OP_hi_user 0xFF + +/* [7.8] Figure 23 "Base type encoding values" (pp. 140-141) in DWARFv3 draft 8 */ + +#define DW_ATE_address 0x1 +#define DW_ATE_boolean 0x2 +#define DW_ATE_complex_float 0x3 +#define DW_ATE_float 0x4 +#define DW_ATE_signed 0x5 +#define DW_ATE_signed_char 0x6 +#define DW_ATE_unsigned 0x7 +#define DW_ATE_unsigned_char 0x8 +#define DW_ATE_imaginary_float 0x9 +#define DW_ATE_lo_user 0x80 +#define DW_ATE_hi_user 0xFF + +/* [7.9] Figure 24 "Accessibility encodings" (p. 141) in DWARFv3 draft 8 */ + +#define DW_ACCESS_public 0x1 +#define DW_ACCESS_protected 0x2 +#define DW_ACCESS_private 0x3 + +/* [7.10] Figure 25 "Visibility encodings" (p. 142) in DWARFv3 draft 8 */ + +#define DW_VIS_local 0x1 +#define DW_VIS_exported 0x2 +#define DW_VIS_qualified 0x3 + +/* [7.11] Figure 26 "Virtuality encodings" (p. 142) in DWARFv3 draft 8 */ + +#define DW_VIRTUALITY_none 0x0 +#define DW_VIRTUALITY_virtual 0x1 +#define DW_VIRTUALITY_pure_virtual 0x2 + + +/* [7.12] Figure 27 "Language encodings" (p. 143) in DWARFv3 draft 8 */ + +#define DW_LANG_C89 0x1 +#define DW_LANG_C 0x2 +#define DW_LANG_Ada83 0x3 +#define DW_LANG_C_plus_plus 0x4 +#define DW_LANG_Cobol74 0x5 +#define DW_LANG_Cobol85 0x6 +#define DW_LANG_Fortran77 0x7 +#define DW_LANG_Fortran90 0x8 +#define DW_LANG_Pascal83 0x9 +#define DW_LANG_Modula2 0xA +#define DW_LANG_Java 0xB +#define DW_LANG_C99 0xC +#define DW_LANG_Ada95 0xD +#define DW_LANG_Fortran95 0xE +#define DW_LANG_PLI 0xF +#define DW_LANG_lo_user 0x8000 +#define DW_LANG_hi_user 0xFFFF + +/* [7.13], "Address Class Encodings" (p. 144) in DWARFv3 draft 8 */ + +#define DW_ADDR_none 0x0 + +/* [7.14] Figure 28 "Identifier case encodings" (p. 144) in DWARFv3 draft 8 */ + +#define DW_ID_case_sensitive 0x0 +#define DW_ID_up_case 0x1 +#define DW_ID_down_case 0x2 +#define DW_ID_case_insensitive 0x3 + +/* [7.15] Figure 29 "Calling convention encodings" (p. 144) in DWARFv3 draft 8 */ + +#define DW_CC_normal 0x1 +#define DW_CC_program 0x2 +#define DW_CC_nocall 0x3 +#define DW_CC_lo_user 0x40 +#define DW_CC_hi_user 0xFF + +/* [7.16] Figure 30 "Inline encodings" (p. 145) in DWARFv3 draft 8 */ + +#define DW_INL_not_inlined 0x0 +#define DW_INL_inlined 0x1 +#define DW_INL_declared_not_inlined 0x2 +#define DW_INL_declared_inlined 0x3 + +/* [7.17] Figure 31 "Ordering encodings" (p. 145) in DWARFv3 draft 8 */ + +#define DW_ORD_row_major 0x0 +#define DW_ORD_col_major 0x1 + +/* [7.18] Figure 32 "Discriminant descriptor encodings" (p. 146) in DWARFv3 draft 8 */ + +#define DW_DSC_label 0x0 +#define DW_DSC_range 0x1 + +/* [7.21] Figure 33 "Line Number Standard Opcode Encodings" (pp. 148-149) in DWARFv3 draft 8 */ + +#define DW_LNS_copy 0x1 +#define DW_LNS_advance_pc 0x2 +#define DW_LNS_advance_line 0x3 +#define DW_LNS_set_file 0x4 +#define DW_LNS_set_column 0x5 +#define DW_LNS_negate_stmt 0x6 +#define DW_LNS_set_basic_block 0x7 +#define DW_LNS_const_add_pc 0x8 +#define DW_LNS_fixed_advance_pc 0x9 +#define DW_LNS_set_prologue_end 0xA +#define DW_LNS_set_epilogue_begin 0xB +#define DW_LNS_set_isa 0xC + +/* [7.21] Figure 34 "Line Number Extended Opcode Encodings" (p. 149) in DWARFv3 draft 8 */ + +#define DW_LNE_end_sequence 0x1 +#define DW_LNE_set_address 0x2 +#define DW_LNE_define_file 0x3 +#define DW_LNE_lo_user 0x80 +#define DW_LNE_hi_user 0xFF + +/* [7.22] Figure 35 "Macinfo Type Encodings" (p. 150) in DWARFv3 draft 8 */ + +#define DW_MACINFO_define 0x1 +#define DW_MACINFO_undef 0x2 +#define DW_MACINFO_start_file 0x3 +#define DW_MACINFO_end_file 0x4 +#define DW_MACINFO_vendor_ext 0xFF + +/* [7.23] Figure 36 "Call frame instruction encodings" (pp. 151-152) in DWARFv3 draft 8 */ + +#define DW_CFA_advance_loc 0x40 // high 2 bits are 0x1, lower 6 bits are delta +#define DW_CFA_offset 0x80 // high 2 bits are 0x2, lower 6 bits are register +#define DW_CFA_restore 0xC0 // high 2 bits are 0x3, lower 6 bits are register +#define DW_CFA_nop 0x0 +#define DW_CFA_set_loc 0x1 +#define DW_CFA_advance_loc1 0x2 +#define DW_CFA_advance_loc2 0x3 +#define DW_CFA_advance_loc4 0x4 +#define DW_CFA_offset_extended 0x5 +#define DW_CFA_restore_extended 0x6 +#define DW_CFA_undefined 0x7 +#define DW_CFA_same_value 0x8 +#define DW_CFA_register 0x9 +#define DW_CFA_remember_state 0xA +#define DW_CFA_restore_state 0xB +#define DW_CFA_def_cfa 0xC +#define DW_CFA_def_cfa_register 0xD +#define DW_CFA_def_cfa_offset 0xE +#define DW_CFA_def_cfa_expression 0xF +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 +#define DW_CFA_lo_user 0x1C +#define DW_CFA_hi_user 0x3F + +/* FSF exception handling Pointer-Encoding constants (CFI augmentation) -- "DW_EH_PE_..." in the FSF sources */ + +#define DW_GNU_EH_PE_absptr 0x0 +#define DW_GNU_EH_PE_uleb128 0x1 +#define DW_GNU_EH_PE_udata2 0x2 +#define DW_GNU_EH_PE_udata4 0x3 +#define DW_GNU_EH_PE_udata8 0x4 +#define DW_GNU_EH_PE_sleb128 0x9 +#define DW_GNU_EH_PE_sdata2 0xA +#define DW_GNU_EH_PE_sdata4 0xB +#define DW_GNU_EH_PE_sdata8 0xC +#define DW_GNU_EH_PE_signed 0x8 +#define DW_GNU_EH_PE_MASK_ENCODING 0x0F +#define DW_GNU_EH_PE_pcrel 0x10 +#define DW_GNU_EH_PE_textrel 0x20 +#define DW_GNU_EH_PE_datarel 0x30 +#define DW_GNU_EH_PE_funcrel 0x40 +#define DW_GNU_EH_PE_aligned 0x50 +#define DW_GNU_EH_PE_indirect 0x80 +#define DW_GNU_EH_PE_omit 0xFF + +#endif // DebugBase_dwarf_h_ diff --git a/lldb/include/lldb/Expression/ClangASTSource.h b/lldb/include/lldb/Expression/ClangASTSource.h new file mode 100644 index 000000000000..4f1e5631525b --- /dev/null +++ b/lldb/include/lldb/Expression/ClangASTSource.h @@ -0,0 +1,67 @@ +/* + * ClangASTSource.h + * lldb + * + * Created by John McCall on 6/1/10. + * Copyright 2010 Apple. All rights reserved. + * + */ +#ifndef liblldb_ClangASTSource_h_ +#define liblldb_ClangASTSource_h_ + +#include "clang/Sema/ExternalSemaSource.h" + +namespace lldb_private { + +class ClangExpressionDeclMap; + +class ClangASTSource : public clang::ExternalSemaSource { + clang::ASTContext &Context; + ClangExpressionDeclMap &DeclMap; +public: + friend struct NameSearchContext; + + ClangASTSource(clang::ASTContext &Context, + ClangExpressionDeclMap &DeclMap) : + Context(Context), + DeclMap(DeclMap) {} + ~ClangASTSource(); + + clang::Decl *GetExternalDecl(uint32_t); + clang::Stmt *GetExternalDeclStmt(uint64_t); + + clang::Selector GetExternalSelector(uint32_t); + uint32_t GetNumExternalSelectors(); + + clang::DeclContext::lookup_result FindExternalVisibleDeclsByName(const clang::DeclContext *DC, + clang::DeclarationName Name); + + bool FindExternalLexicalDecls(const clang::DeclContext *DC, + llvm::SmallVectorImpl &Decls); + + void StartTranslationUnit(clang::ASTConsumer *Consumer); +}; + +// API for ClangExpressionDeclMap +struct NameSearchContext { + ClangASTSource &ASTSource; + llvm::SmallVectorImpl &Decls; + clang::DeclarationName &Name; + const clang::DeclContext *DC; + + NameSearchContext (ClangASTSource &ASTSource, + llvm::SmallVectorImpl &Decls, + clang::DeclarationName &Name, + const clang::DeclContext *DC) : + ASTSource(ASTSource), + Decls(Decls), + Name(Name), + DC(DC) {} + + clang::ASTContext *GetASTContext(); + clang::NamedDecl *AddVarDecl(void *type); +}; + +} + +#endif \ No newline at end of file diff --git a/lldb/include/lldb/Expression/ClangExpression.h b/lldb/include/lldb/Expression/ClangExpression.h new file mode 100644 index 000000000000..a645abf88299 --- /dev/null +++ b/lldb/include/lldb/Expression/ClangExpression.h @@ -0,0 +1,124 @@ +//===-- 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 + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" + +namespace llvm +{ + class ExecutionEngine; + class StringRef; +} + +namespace lldb_private { + +class RecordingMemoryManager; + +class ClangExpression +{ +public: + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ClangExpression(const char *target_triple, + ClangExpressionDeclMap *expr_decl_map); + + ~ClangExpression(); + + unsigned Compile(); + + unsigned + ParseExpression (const char *expr_text, Stream &stream); + + unsigned + ParseBareExpression (llvm::StringRef expr_text, Stream &stream); + + unsigned + ConvertExpressionToDWARF (ClangExpressionVariableList &expr_local_variable_list, + StreamString &dwarf_opcode_strm); + + bool + JITFunction (const ExecutionContext &exc_context, const char *func_name); + + bool + WriteJITCode (const ExecutionContext &exc_context); + + lldb::addr_t + GetFunctionAddress (const char *name); + + clang::CompilerInstance * + GetCompilerInstance () + { + return m_clang_ap.get(); + } + + clang::ASTContext * + GetASTContext (); + + static Mutex & + GetClangMutex (); +protected: + + // This class is a pass-through for the default JIT memory manager, + // which just records the memory regions that were handed out so we + // can copy them into the target later on. + + + //------------------------------------------------------------------ + // Classes that inherit from ClangExpression can see and modify these + //------------------------------------------------------------------ + + struct JittedFunction { + std::string m_name; + lldb::addr_t m_local_addr; + lldb::addr_t m_remote_addr; + + 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) {} + }; + + std::string m_target_triple; + ClangExpressionDeclMap *m_decl_map; + std::auto_ptr m_clang_ap; + clang::CodeGenerator *m_code_generator_ptr; // This will be deleted by the Execution Engine. + RecordingMemoryManager *m_jit_mm_ptr; // This will be deleted by the Execution Engine. + std::auto_ptr m_execution_engine; + std::vector m_jitted_functions; +private: + + bool CreateCompilerInstance(bool &IsAST); + + //------------------------------------------------------------------ + // For ClangExpression only + //------------------------------------------------------------------ + ClangExpression(const ClangExpression&); + const ClangExpression& operator=(const ClangExpression&); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpression_h_ diff --git a/lldb/include/lldb/Expression/ClangExpressionDeclMap.h b/lldb/include/lldb/Expression/ClangExpressionDeclMap.h new file mode 100644 index 000000000000..e1c9dc4ebc33 --- /dev/null +++ b/lldb/include/lldb/Expression/ClangExpressionDeclMap.h @@ -0,0 +1,71 @@ +//===-- 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 "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" + +namespace clang { + class DeclarationName; + class DeclContext; +} + +namespace lldb_private { + +class NameSearchContext; +class Variable; + +class ClangExpressionDeclMap +{ +public: + ClangExpressionDeclMap(ExecutionContext *exe_ctx); + ~ClangExpressionDeclMap(); + + // Interface for ClangStmtVisitor + bool GetIndexForDecl (uint32_t &index, + const clang::Decl *decl); + + // Interface for DwarfExpression + Value *GetValueForIndex (uint32_t index); + + // Interface for ClangASTSource + void GetDecls (NameSearchContext &context, + const char *name); +protected: +private: + struct Tuple + { + const clang::NamedDecl *m_decl; + Value *m_value; /* owned by ClangExpressionDeclMap */ + }; + + typedef std::vector TupleVector; + typedef TupleVector::iterator TupleIterator; + + TupleVector m_tuples; + ExecutionContext *m_exe_ctx; + SymbolContext *m_sym_ctx; /* owned by ClangExpressionDeclMap */ + + void AddOneVariable(NameSearchContext &context, Variable* var); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionDeclMap_h_ diff --git a/lldb/include/lldb/Expression/ClangExpressionVariable.h b/lldb/include/lldb/Expression/ClangExpressionVariable.h new file mode 100644 index 000000000000..fc6cccced11a --- /dev/null +++ b/lldb/include/lldb/Expression/ClangExpressionVariable.h @@ -0,0 +1,58 @@ +//===-- 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 + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" + +namespace lldb_private { + +class ClangExpressionVariableList +{ +public: + ClangExpressionVariableList(); + ~ClangExpressionVariableList(); + + Value * + GetVariableForVarDecl (clang::ASTContext &ast_context, + const clang::VarDecl *var_decl, + uint32_t& idx, + bool can_create); + + Value * + GetVariableAtIndex (uint32_t idx); + + uint32_t + AppendValue (Value *value); // takes ownership + +private: + struct ClangExpressionVariable + { + const clang::VarDecl *m_var_decl; + Value *m_value; + }; + + typedef std::vector Variables; + Variables m_variables; +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionVariable_h_ diff --git a/lldb/include/lldb/Expression/ClangFunction.h b/lldb/include/lldb/Expression/ClangFunction.h new file mode 100644 index 000000000000..bf0a81720676 --- /dev/null +++ b/lldb/include/lldb/Expression/ClangFunction.h @@ -0,0 +1,241 @@ +//===-- 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/lldb.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Expression/ClangExpression.h" + +// Right now, this is just a toy. It calls a set function, with fixed +// values. + +namespace clang +{ + class ASTRecordLayout; +} + +namespace lldb_private +{ + +class ClangFunction : private ClangExpression +{ +public: + + typedef enum ExecutionResults + { + eExecutionSetupError, + eExecutionCompleted, + eExecutionDiscarded, + eExecutionInterrupted, + eExecutionTimedOut + }; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + // Usage Note: + + // A given ClangFunction object can handle any function with a common signature. It can also be used to + // set up any number of concurrent functions calls once it has been constructed. + // When you construct it you pass in a particular function, information sufficient to determine the function signature + // and value list. + // The simplest use of the ClangFunction is to construct the function, then call ExecuteFunction (context, errors, results). The function + // will be called using the initial arguments, and the results determined for you, and all cleanup done. + // + // However, if you need to use the function caller in Thread Plans, you need to call the function on the plan stack. + // In that case, you call GetThreadPlanToCallFunction, args_addr will be the location of the args struct, and after you are + // done running this thread plan you can recover the results using FetchFunctionResults passing in the same value. + // You are required to call InsertFunction before calling GetThreadPlanToCallFunction. + // + // You can also reuse the struct if you want, by calling ExecuteFunction but passing in args_addr_ptr primed to this value. + // + // You can also reuse the ClangFunction for the same signature but different function or argument values by rewriting the + // Functions arguments with WriteFunctionArguments, and then calling ExecuteFunction passing in the same args_addr. + // + // Note, any of the functions below 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 functions below that take arg_addr_ptr can be passed NULL, and the argument space will be managed for you. + + ClangFunction(const char *target_triple, Function &function_ptr, ClangASTContext *ast_context, const ValueList &arg_value_list); + // This constructor takes its return type as a Clang QualType opaque pointer, and the ast_context it comes from. + // FIXME: We really should be able to easily make a Type from the qualtype, and then just pass that in. + ClangFunction(const char *target_triple, ClangASTContext *ast_context, void *return_qualtype, const Address& functionAddress, const ValueList &arg_value_list); + virtual ~ClangFunction(); + + unsigned CompileFunction (Stream &errors); + + // args_addr is a pointer to the address the addr will be filled with. If the value on + // input is LLDB_INVALID_ADDRESS then a new address will be allocated, and returned in args_addr. + // If args_addr is a value already returned from a previous call to InsertFunction, then + // the args structure at that address is overwritten. + // If any other value is returned, then we return false, and do nothing. + bool InsertFunction (ExecutionContext &context, lldb::addr_t &args_addr_ref, Stream &errors); + + bool WriteFunctionWrapper (ExecutionContext &exec_ctx, Stream &errors); + + // This variant writes down the original function address and values to args_addr. + bool WriteFunctionArguments (ExecutionContext &exec_ctx, lldb::addr_t &args_addr_ref, Stream &errors); + + // This variant writes down function_address and arg_value. + bool WriteFunctionArguments (ExecutionContext &exc_context, lldb::addr_t &args_addr_ref, Address function_address, ValueList &arg_values, Stream &errors); + + //------------------------------------------------------------------ + /// 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] context + /// 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 &context, 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] context + /// 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 &exc_context, 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 single_thread_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] context + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] single_thread_timeout_usec + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @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 &context, 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] context + /// 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] single_thread_timeout_usec + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @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 &context, lldb::addr_t *args_addr_ptr, Stream &errors, bool stop_others, uint32_t single_thread_timeout_usec, bool try_all_threads, Value &results); + ExecutionResults ExecuteFunctionWithABI(ExecutionContext &context, Stream &errors, Value &results); + + ThreadPlan *GetThreadPlanToCallFunction (ExecutionContext &exc_context, lldb::addr_t &args_addr_ref, Stream &errors, bool stop_others, bool discard_on_error = true); + bool FetchFunctionResults (ExecutionContext &exc_context, lldb::addr_t args_addr, Value &ret_value); + void DeallocateFunctionResults (ExecutionContext &exc_context, lldb::addr_t args_addr); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangFunction can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For ClangFunction only + //------------------------------------------------------------------ + + 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. + void *m_function_return_qual_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; + std::string m_wrapper_struct_name; + const clang::ASTRecordLayout *m_struct_layout; + ValueList m_arg_values; + lldb::addr_t m_wrapper_fun_addr; + std::list m_wrapper_args_addrs; + + size_t m_value_struct_size; + size_t m_return_offset; + uint64_t m_return_size; // Not strictly necessary, could get it from the Function... + bool m_compiled; + bool m_JITted; +}; + +}; // Namespace lldb_private +#endif // lldb_ClangFunction_h_ diff --git a/lldb/include/lldb/Expression/ClangStmtVisitor.h b/lldb/include/lldb/Expression/ClangStmtVisitor.h new file mode 100644 index 000000000000..5f8d6e5936ff --- /dev/null +++ b/lldb/include/lldb/Expression/ClangStmtVisitor.h @@ -0,0 +1,109 @@ +//===-- ClangStmtVisitor.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangStmtVisitor_h_ +#define liblldb_ClangStmtVisitor_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "clang/AST/StmtVisitor.h" + +// Project includes +#include "lldb/Core/ClangForward.h" + +namespace lldb_private { + +class StreamString; +class ClangExpressionDeclMap; +class ClangExpressionVariableList; + +#define CLANG_STMT_RESULT void + +class ClangStmtVisitor : public clang::StmtVisitor +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ClangStmtVisitor (clang::ASTContext &ast_context, + ClangExpressionVariableList &variable_list, + ClangExpressionDeclMap *decl_map, + StreamString &strm); + virtual ~ClangStmtVisitor (); + + // Stmts. + CLANG_STMT_RESULT VisitStmt (clang::Stmt *Node); + CLANG_STMT_RESULT VisitDeclStmt (clang::DeclStmt *Node); + CLANG_STMT_RESULT VisitLabelStmt (clang::LabelStmt *Node); + CLANG_STMT_RESULT VisitGotoStmt (clang::GotoStmt *Node); + + // Exprs + CLANG_STMT_RESULT VisitExpr (clang::Expr *Node); + CLANG_STMT_RESULT VisitDeclRefExpr (clang::DeclRefExpr *Node); + CLANG_STMT_RESULT VisitPredefinedExpr (clang::PredefinedExpr *Node); + CLANG_STMT_RESULT VisitCharacterLiteral (clang::CharacterLiteral *Node); + CLANG_STMT_RESULT VisitIntegerLiteral (clang::IntegerLiteral *Node); + CLANG_STMT_RESULT VisitFloatingLiteral (clang::FloatingLiteral *Node); + CLANG_STMT_RESULT VisitStringLiteral (clang::StringLiteral *Str); + CLANG_STMT_RESULT VisitUnaryOperator (clang::UnaryOperator *Node); + CLANG_STMT_RESULT VisitSizeOfAlignOfExpr (clang::SizeOfAlignOfExpr *Node); + CLANG_STMT_RESULT VisitMemberExpr (clang::MemberExpr *Node); + CLANG_STMT_RESULT VisitExtVectorElementExpr (clang::ExtVectorElementExpr *Node); + CLANG_STMT_RESULT VisitBinaryOperator (clang::BinaryOperator *Node); +// CLANG_STMT_RESULT VisitCompoundAssignOperator (clang::CompoundAssignOperator *Node); + CLANG_STMT_RESULT VisitAddrLabelExpr (clang::AddrLabelExpr *Node); + CLANG_STMT_RESULT VisitTypesCompatibleExpr (clang::TypesCompatibleExpr *Node); + CLANG_STMT_RESULT VisitParenExpr(clang::ParenExpr *Node); + CLANG_STMT_RESULT VisitInitListExpr (clang::InitListExpr *Node); + CLANG_STMT_RESULT VisitCastExpr (clang::CastExpr *Node); +// CLANG_STMT_RESULT VisitImplicitCastExpr (clang::ImplicitCastExpr *Node); + CLANG_STMT_RESULT VisitArraySubscriptExpr (clang::ArraySubscriptExpr *Node); + // C++ + CLANG_STMT_RESULT VisitCXXNamedCastExpr (clang::CXXNamedCastExpr *Node); + CLANG_STMT_RESULT VisitCXXBoolLiteralExpr (clang::CXXBoolLiteralExpr *Node); + CLANG_STMT_RESULT VisitCXXThisExpr (clang::CXXThisExpr *Node); + CLANG_STMT_RESULT VisitCXXFunctionalCastExpr (clang::CXXFunctionalCastExpr *Node); + + // ObjC + CLANG_STMT_RESULT VisitObjCEncodeExpr (clang::ObjCEncodeExpr *Node); + CLANG_STMT_RESULT VisitObjCMessageExpr (clang::ObjCMessageExpr* Node); + CLANG_STMT_RESULT VisitObjCSelectorExpr (clang::ObjCSelectorExpr *Node); + CLANG_STMT_RESULT VisitObjCProtocolExpr (clang::ObjCProtocolExpr *Node); + CLANG_STMT_RESULT VisitObjCPropertyRefExpr (clang::ObjCPropertyRefExpr *Node); + CLANG_STMT_RESULT VisitObjCImplicitSetterGetterRefExpr (clang::ObjCImplicitSetterGetterRefExpr *Node); + CLANG_STMT_RESULT VisitObjCIvarRefExpr (clang::ObjCIvarRefExpr *Node); + CLANG_STMT_RESULT VisitObjCSuperExpr (clang::ObjCSuperExpr *Node); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangStmtVisitor can see and modify these + //------------------------------------------------------------------ + clang::ASTContext &m_ast_context; + ClangExpressionDeclMap *m_decl_map; + ClangExpressionVariableList &m_variable_list; + StreamString &m_stream; +private: + //------------------------------------------------------------------ + // For ClangStmtVisitor only + //------------------------------------------------------------------ + ClangStmtVisitor (const ClangStmtVisitor&); + const ClangStmtVisitor& operator= (const ClangStmtVisitor&); + + bool + EncodeUInt64 (uint64_t uval, uint32_t bit_size); + + bool + EncodeSInt64 (int64_t sval, uint32_t bit_size); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangStmtVisitor_h_ diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h new file mode 100644 index 000000000000..072697659fa0 --- /dev/null +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -0,0 +1,126 @@ +//===-- 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" + +class ClangExpressionVariable; +class ClangExpressionVariableList; + +namespace lldb_private { + +class ClangExpressionDeclMap; + +//---------------------------------------------------------------------- +// A class designed to evaluate the DWARF expression opcodes. We will +// likely augment its abilities to handle features not supported by +// the DWARF expression engine. +//---------------------------------------------------------------------- +class DWARFExpression +{ +public: + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + DWARFExpression(); + + DWARFExpression(const DataExtractor& data, + uint32_t data_offset, + uint32_t data_length, + const Address* loclist_base_addr_ptr); + + DWARFExpression(const DWARFExpression& rhs); + + virtual + ~DWARFExpression(); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + bool + IsValid() const; + + bool + IsLocationList() const; + + bool + LocationListContainsLoadAddress (Process* process, const Address &addr) const; + + void + SetOpcodeData(const DataExtractor& data, const Address* loclist_base_addr_ptr); + + void + SetOpcodeData(const DataExtractor& data, uint32_t data_offset, uint32_t data_length, const Address* loclist_base_addr_ptr); + + void + SetLocationListBaseAddress(Address& base_addr); + + int + GetRegisterKind (); + + void + SetRegisterKind (int reg_kind); + + bool + Evaluate (ExecutionContextScope *exe_scope, + clang::ASTContext *ast_context, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr) const; + + bool + Evaluate (ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr) const; + + static bool + Evaluate (ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + const DataExtractor& opcodes, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + const uint32_t offset, + const uint32_t length, + const uint32_t reg_set, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr); + + void + SetExpressionLocalVariableList (ClangExpressionVariableList *locals); + + void + SetExpressionDeclMap (ClangExpressionDeclMap *decl_map); + +protected: + + void DumpLocation(Stream *s, uint32_t offset, uint32_t length, lldb::DescriptionLevel level) const; + //------------------------------------------------------------------ + // Classes that inherit from DWARFExpression can see and modify these + //------------------------------------------------------------------ + DataExtractor m_data; + int m_reg_kind; // One of the defines that starts with LLDB_REGKIND_ + Address m_loclist_base_addr; // Base address needed for location lists + ClangExpressionVariableList *m_expr_locals; + ClangExpressionDeclMap *m_decl_map; +}; + +} // namespace lldb_private + +#endif // liblldb_DWARFExpression_h_ diff --git a/lldb/include/lldb/Expression/RecordingMemoryManager.h b/lldb/include/lldb/Expression/RecordingMemoryManager.h new file mode 100644 index 000000000000..8ebc08e11584 --- /dev/null +++ b/lldb/include/lldb/Expression/RecordingMemoryManager.h @@ -0,0 +1,154 @@ +//===-- RecordingMemoryManager.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_RecordingMemoryManager_h_ +#define lldb_RecordingMemoryManager_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 "llvm/ExecutionEngine/JITMemoryManager.h" +#include "lldb/Expression/ClangExpression.h" + +namespace lldb_private { + +class RecordingMemoryManager : public llvm::JITMemoryManager +{ + +// I can't write the JIT code in this class because this class has to be +// built without RTTI, which means I can't include Process.h. But I don't +// want to write iterators or "do_over_regions" functions right now, so I'm +// just going to let the ClangExpression handle it using our data members directly. + +friend bool ClangExpression::WriteJITCode (const ExecutionContext &exc_context); + +public: + RecordingMemoryManager (); + virtual ~RecordingMemoryManager(); + + virtual void setMemoryWritable (); + + virtual void setMemoryExecutable (); + + virtual void setPoisonMemory (bool poison) + { + m_default_mm_ap->setPoisonMemory (poison); + } + + virtual void AllocateGOT() + { + m_default_mm_ap->AllocateGOT(); + } + + + virtual uint8_t *getGOTBase() const + { + return m_default_mm_ap->getGOTBase(); + } + + virtual uint8_t *startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize); + + virtual uint8_t *allocateStub(const llvm::GlobalValue* F, unsigned StubSize, + unsigned Alignment); + + virtual void endFunctionBody(const llvm::Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd); + + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); + + virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment); + + virtual void deallocateFunctionBody(void *Body); + + virtual uint8_t* startExceptionTable(const llvm::Function* F, + uintptr_t &ActualSize); + + virtual void endExceptionTable(const llvm::Function *F, uint8_t *TableStart, + uint8_t *TableEnd, uint8_t* FrameRegister); + + virtual void deallocateExceptionTable(void *ET); + + virtual size_t GetDefaultCodeSlabSize() { + return m_default_mm_ap->GetDefaultCodeSlabSize(); + } + + virtual size_t GetDefaultDataSlabSize() { + return m_default_mm_ap->GetDefaultDataSlabSize(); + } + + virtual size_t GetDefaultStubSlabSize() { + return m_default_mm_ap->GetDefaultStubSlabSize(); + } + + virtual unsigned GetNumCodeSlabs() { + return m_default_mm_ap->GetNumCodeSlabs(); + } + + virtual unsigned GetNumDataSlabs() { + return m_default_mm_ap->GetNumDataSlabs(); + } + + virtual unsigned GetNumStubSlabs() { + return m_default_mm_ap->GetNumStubSlabs(); + } + + // These are methods I've added so we can transfer the memory we've remembered down + // to the target program. For now I'm assuming all this code is PIC without fixups, + // so I'll just copy it blind, but if we need to we can do fixups later. + + lldb::addr_t + GetRemoteAddressForLocal (lldb::addr_t local_address); + + bool + WriteJITRegions (const ExecutionContext &exc_context); + + +private: + std::auto_ptr m_default_mm_ap; + std::map m_functions; + std::map m_spaceBlocks; + std::map m_stubs; + std::map m_globals; + std::map m_exception_tables; + + struct LocalToRemoteAddressRange + { + lldb::addr_t m_local_start; + size_t m_size; + lldb::addr_t m_remote_start; + + LocalToRemoteAddressRange (lldb::addr_t lstart, size_t size, lldb::addr_t rstart) : + m_local_start (lstart), + m_size (size), + m_remote_start (rstart) + {} + + }; + + void + AddToLocalToRemoteMap (lldb::addr_t lstart, size_t size, lldb::addr_t rstart); + + // We should probably store this by address so we can efficiently + // search it but there really won't be many elements in this array + // at present. So we can put that off for now. + std::vector m_address_map; + +}; + +}; // namespace lldb_private +#endif // lldb_RecordingMemoryManager_h_ diff --git a/lldb/include/lldb/Host/Condition.h b/lldb/include/lldb/Host/Condition.h new file mode 100644 index 000000000000..5ae50ae3fac3 --- /dev/null +++ b/lldb/include/lldb/Host/Condition.h @@ -0,0 +1,123 @@ +//===-- 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 + +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 (); + + //------------------------------------------------------------------ + /// Get accessor to the pthread condition object. + /// + /// @return + /// A pointer to the condition variable owned by this object. + //------------------------------------------------------------------ + pthread_cond_t * + GetCondition (); + + //------------------------------------------------------------------ + /// 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 (pthread_mutex_t *mutex, const TimeValue *abstime = NULL, bool *timed_out = NULL); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + pthread_cond_t m_condition; ///< The condition variable. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif + diff --git a/lldb/include/lldb/Host/Endian.h b/lldb/include/lldb/Host/Endian.h new file mode 100644 index 000000000000..5d1e848ef31d --- /dev/null +++ b/lldb/include/lldb/Host/Endian.h @@ -0,0 +1,19 @@ +//===-- 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_ + +// TODO: come up with a more lldb specific solution instead of relying on +// macosx .... + +#include + +#endif // liblldb_host_endian_h_ + diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h new file mode 100644 index 000000000000..5f52085ae6d3 --- /dev/null +++ b/lldb/include/lldb/Host/Host.h @@ -0,0 +1,281 @@ +//===-- 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 "lldb/lldb-private.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, + 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 unique handle to the process monitoring information that + /// can be used to stop monitoring a child process. + /// + /// @see static void Host::StopMonitoringChildProcess (uint32_t) + //------------------------------------------------------------------ + static uint32_t + StartMonitoringChildProcess (MonitorChildProcessCallback callback, + void *callback_baton, + lldb::pid_t pid, + bool monitor_signals); + + //------------------------------------------------------------------ + /// Stop monitoring a child process. + /// + /// @param[in] handle + /// A unique handle returned from a previous call to + /// Host::StartMonitoringChildProcess(...). + /// + /// @return + /// \b true if the the handle was found and disabled, \b false + /// if the monitor map with handle of \a handle was not found. + /// + /// @see static int Host::StartMonitoringChildProcess (MonitorChildProcessCallback *, void *, lldb::pid_t, bool) + //------------------------------------------------------------------ + static bool + StopMonitoringChildProcess (uint32_t handle); + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// Gets the host kernel architecture. + /// + /// @return + /// A const architecture object that represents the host kernel + /// architecture. + //------------------------------------------------------------------ + static const ArchSpec & + GetArchitecture (); + + //------------------------------------------------------------------ + /// 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::pid_t + GetCurrentThreadID (); + + 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 NULL terminate C string name that is owned by a static + /// global string pool, or NULL if there is no matching thread + /// name. This string does not need to be freed. + //------------------------------------------------------------------ + static const char * + 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 void + SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name); + + //------------------------------------------------------------------ + /// 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); + + static bool + ResolveExecutableInBundle (FileSpec *file); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Host_h_ diff --git a/lldb/include/lldb/Host/Mutex.h b/lldb/include/lldb/Host/Mutex.h new file mode 100644 index 000000000000..19f613a86f6a --- /dev/null +++ b/lldb/include/lldb/Host/Mutex.h @@ -0,0 +1,242 @@ +//===-- 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 + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Mutex Mutex.h "lldb/Host/Mutex.h" +/// @brief A C++ wrapper class for pthread mutexes. +//---------------------------------------------------------------------- +class Mutex +{ +public: + 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); + + //-------------------------------------------------------------- + /// Constructor with a raw pthread mutex object pointer. + /// + /// This will create a scoped mutex locking object that locks + /// \a mutex. + /// + /// @param[in] mutex + /// A pointer to a pthread_mutex_t that will get locked if + /// non-NULL. + //-------------------------------------------------------------- + Locker(pthread_mutex_t *mutex); + + //-------------------------------------------------------------- + /// 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 + Reset(pthread_mutex_t *mutex = NULL); + + //-------------------------------------------------------------- + /// 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 (pthread_mutex_t *mutex); + + protected: + //-------------------------------------------------------------- + /// Member variables + //-------------------------------------------------------------- + pthread_mutex_t *m_mutex_ptr; ///< A pthread mutex that is locked when + ///< acquired and unlocked when destroyed + ///< or reset. + + 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. + //------------------------------------------------------------------ + ~Mutex(); + + //------------------------------------------------------------------ + /// Mutex get accessor. + /// + /// @return + /// A pointer to the pthread mutex object owned by this object. + //------------------------------------------------------------------ + pthread_mutex_t * + GetMutex(); + + //------------------------------------------------------------------ + /// 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(). + //------------------------------------------------------------------ + 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(). + //------------------------------------------------------------------ + int + TryLock(); + + //------------------------------------------------------------------ + /// 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(). + //------------------------------------------------------------------ + int + Unlock(); + + static + int Lock (pthread_mutex_t *mutex_ptr); + + static + int TryLock (pthread_mutex_t *mutex_ptr); + + static + int Unlock (pthread_mutex_t *mutex_ptr); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + pthread_mutex_t m_mutex; ///< The pthread mutex object. +private: + Mutex(const Mutex&); + const Mutex& operator=(const Mutex&); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif diff --git a/lldb/include/lldb/Host/Predicate.h b/lldb/include/lldb/Host/Predicate.h new file mode 100644 index 000000000000..9c3f10bceaeb --- /dev/null +++ b/lldb/include/lldb/Host/Predicate.h @@ -0,0 +1,411 @@ +//===-- 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_mutex(), + m_condition(), + m_value() + { + } + + //------------------------------------------------------------------ + /// 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_mutex(), + m_condition(), + m_value(initial_value) + { + } + + //------------------------------------------------------------------ + /// 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)", __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)", __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)", __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. + /// + /// @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", __FUNCTION__, bits, abstime, m_value); +#endif + while (err == 0 && ((m_value & bits) == 0)) + { + err = m_condition.Wait (m_mutex.GetMutex(), abstime); + } +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x", __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. + /// + /// @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", __FUNCTION__, bits, abstime, m_value); +#endif + while (err == 0 && (m_value & bits != 0)) + { + err = m_condition.Wait (m_mutex.GetMutex(), abstime); + } + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x", __FUNCTION__, bits, m_value); +#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. + /// + /// @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", __FUNCTION__, value, abstime, m_value); +#endif + while (err == 0 && m_value != value) + { + err = m_condition.Wait (m_mutex.GetMutex(), abstime, timed_out); + } + + return m_value == value; + } + + //------------------------------------------------------------------ + /// 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. + /// + /// @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", __FUNCTION__, value, abstime, m_value); +#endif + while (err == 0 && m_value == value) + { + err = m_condition.Wait (m_mutex.GetMutex(), 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", __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/lldb/include/lldb/Host/Symbols.h b/lldb/include/lldb/Host/Symbols.h new file mode 100644 index 000000000000..7359dda8a6d0 --- /dev/null +++ b/lldb/include/lldb/Host/Symbols.h @@ -0,0 +1,37 @@ +//===-- 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/Core/FileSpec.h" + +namespace lldb_private { + +class Symbols +{ +public: + static FileSpec + LocateExecutableObjectFile (const FileSpec *in_exec, const ArchSpec* arch, const UUID *uuid); + + static FileSpec + LocateExecutableSymbolFile (const FileSpec *in_exec, const ArchSpec* arch, const UUID *uuid); +}; + +} // namespace lldb_private + + +#endif // liblldb_Symbols_h_ diff --git a/lldb/include/lldb/Host/TimeValue.h b/lldb/include/lldb/Host/TimeValue.h new file mode 100644 index 000000000000..623660e29088 --- /dev/null +++ b/lldb/include/lldb/Host/TimeValue.h @@ -0,0 +1,90 @@ +//===-- 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 + +// C++ Includes +// Other libraries and framework includes +// Project includes + +namespace lldb_private { + +class TimeValue +{ +public: + //------------------------------------------------------------------ + // 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; + + struct timespec + GetAsTimeSpec () const; + + struct timeval + GetAsTimeVal () const; + + bool + IsValid () const; + + void + OffsetWithSeconds (uint32_t sec); + + void + OffsetWithMicroSeconds (uint32_t usec); + + void + OffsetWithNanoSeconds (uint32_t nsec); + + static TimeValue + Now(); + +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/lldb/include/lldb/Host/Types.h b/lldb/include/lldb/Host/Types.h new file mode 100644 index 000000000000..1fa791cf692f --- /dev/null +++ b/lldb/include/lldb/Host/Types.h @@ -0,0 +1,98 @@ +//===-- Types.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#if 0 +#ifndef liblldb_host_types_h_ +#define liblldb_host_types_h_ + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// MACOSX START +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_RTTI + +//---------------------------------------------------------------------- +// And source files that may not have RTTI enabled during their +// compilation will want to do a "#define NO_RTTI" before including the +// lldb-include.h file. +//---------------------------------------------------------------------- + +#include // for std::tr1::shared_ptr + +#endif + +//---------------------------------------------------------------------- +// All host systems must define: +// liblldb::condition_t The native condition type (or a substitute class) for conditions on the host system. +// liblldb::mutex_t The native mutex type for mutex objects on the host system. +// liblldb::thread_t The native thread type for spawned threads on the system +// liblldb::thread_arg_t The type of the one any only thread creation argument for the host system +// liblldb::thread_result_t The return type that gets returned when a thread finishes. +// liblldb::thread_func_t The function prototype used to spawn a thread on the host system. +// liblldb::SharedPtr The template that wraps up the host version of a reference counted pointer (like boost::shared_ptr) +// #define LLDB_INVALID_PROCESS_ID ... +// #define LLDB_INVALID_THREAD_ID ... +// #define LLDB_INVALID_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_private { + //---------------------------------------------------------------------- + // 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 + +#ifndef NO_RTTI + // The template below can be used in a few useful ways: + // + // // Make a single shared pointer a class Foo + // lldb::SharePtr::Type foo_sp; + // + // // Make a typedef to a Foo shared pointer + // typedef lldb::SharePtr::Type FooSP; + // + template + struct SharedPtr + { + typedef std::tr1::shared_ptr<_Tp> Type; + }; +#endif + +} // namespace lldb_private + +#define LLDB_INVALID_HOST_THREAD ((lldb::thread_t)NULL) +#define LLDB_INVALID_HOST_TIME { 0, 0 } + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// MACOSX END +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +#endif // liblldb_host_types_h_ +#endif diff --git a/lldb/include/lldb/Interpreter/CommandCompletions.h b/lldb/include/lldb/Interpreter/CommandCompletions.h new file mode 100644 index 000000000000..98cba48f08ab --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandCompletions.h @@ -0,0 +1,240 @@ +//===-- 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 +// 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) (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::CommandInterpreter *interpreter, // The command interpreter running this command. + lldb_private::SearchFilter *searcher, // A search filter to limit the search... + lldb_private::StringList &matches); // The array of matches we return. + typedef enum + { + eNoCompletion = 0, + eSourceFileCompletion = (1 << 0), + eDiskFileCompletion = (1 << 1), + eSymbolCompletion = (1 << 2), + eModuleCompletion = (1 << 3), + eCustomCompletion = (1 << 4) // 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. + + } CommonCompletionTypes; + + struct CommonCompletionElement + { + CommonCompletionTypes type; + CompletionCallback callback; + }; + + static bool InvokeCommonCompletionCallbacks (uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + StringList &matches); + + //---------------------------------------------------------------------- + // These are the generic completer functions: + //---------------------------------------------------------------------- + static int + SourceFiles (const char *partial_file_name, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + SearchFilter *searcher, + StringList &matches); + + static int + Modules (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches); + + static int + Symbols (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + 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 (const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + 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: + std::string m_completion_str; + int m_match_start_point; + int m_max_return_elements; + CommandInterpreter *m_interpreter; + StringList &m_matches; + private: + DISALLOW_COPY_AND_ASSIGN (Completer); + }; + + //---------------------------------------------------------------------- + // SouceFileCompleter implements the source file completer + //---------------------------------------------------------------------- + class SourceFileCompleter : public Completer + { + public: + + SourceFileCompleter (bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + 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 (const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + 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 (const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + 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/lldb/include/lldb/Interpreter/CommandContext.h b/lldb/include/lldb/Interpreter/CommandContext.h new file mode 100644 index 000000000000..69bd27e24721 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandContext.h @@ -0,0 +1,43 @@ +//===-- CommandContext.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandContext_h_ +#define liblldb_CommandContext_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/ValueObjectList.h" + +namespace lldb_private { + +class CommandContext +{ +public: + CommandContext (); + + ~CommandContext (); + + void + Update (ExecutionContext *override_context = NULL); + + Target * + GetTarget(); + + ExecutionContext & + GetExecutionContext(); + +private: + ExecutionContext m_exe_ctx; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandContext_h_ diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h new file mode 100644 index 000000000000..933fce47ff00 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -0,0 +1,264 @@ +//===-- 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/Log.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/StateVariable.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/Args.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +class CommandInterpreter : public Broadcaster +{ +public: + typedef std::map VariableMap; + typedef std::map OptionArgMap; + + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2) // User entered quit + }; + + void + SourceInitFile (bool in_cwd, CommandReturnObject &result); + + CommandInterpreter (lldb::ScriptLanguage script_language, + bool synchronous_execution, + Listener *listener, // In case this is asked to create or attach to a process + SourceManager& source_manager); + + virtual + ~CommandInterpreter (); + + lldb::CommandObjectSP + GetCommandSP (const char *cmd, bool include_aliases = true, bool exact = true, StringList *matches = NULL); + + CommandObject * + GetCommandObject (const char *cmd, bool include_aliases = true, bool exact = true, StringList *matches = NULL); + + StateVariable * + GetStateVariable(const char *name); + + 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 + RemoveUser (const char *alias_name); + + OptionArgVectorSP + GetAliasOptions (const char *alias_name); + + void + RemoveAliasOptions (const char *alias_name); + + void + AddOrReplaceAliasOptions (const char *alias_name, OptionArgVectorSP &option_arg_vector_sp); + + bool + HandleCommand (const char *command_line, bool add_to_history, CommandReturnObject &result, + ExecutionContext *override_context = NULL); + + // 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 Args 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 the total number of completions, or -1 if the completion character should be inserted, or + // INT_MAX if the number of matches is > max_return_elements, but it is expensive to compute. + // + // 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. + + int + HandleCompletionMatches (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + StringList &matches); + + + int + GetCommandNamesMatchingPartialString (const char *cmd_cstr, bool include_aliases, StringList &matches); + + void + GetHelp (CommandReturnObject &result); + + 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, + uint32_t max_word_len); + + void + ShowVariableValues (CommandReturnObject &result); + + void + ShowVariableHelp (CommandReturnObject &result); + + CommandContext * + Context(); + + const Args * + GetProgramArguments (); + + const Args * + GetEnvironmentVariables (); + + const char * + ProcessEmbeddedScriptCommands (const char *arg); + + Listener * + GetListener (); + + SourceManager & + GetSourceManager (); + + const char * + GetPrompt (); + + void + SetPrompt (const char *); + + void + LoadCommandDictionary (); + + void + Initialize (); + + void + InitializeVariables (); + + void + CrossRegisterCommand (const char * dest_cmd, const char * object_type); + + void + SetScriptLanguage (lldb::ScriptLanguage lang); + + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasUserCommands (); + + bool + HasAliasOptions (); + + bool + HasInterpreterVariables (); + + void + BuildAliasCommandArgs (CommandObject *alias_cmd_obj, const char *alias_name, Args &cmd_args, + CommandReturnObject &result); + + int + GetOptionArgumentPosition (const char *in_string); + + ScriptInterpreter * + GetScriptInterpreter (); + + bool + GetSynchronous (); + +#ifndef SWIG + void + AddLogChannel (const char *name, const Log::Callbacks &log_callbacks); + + bool + GetLogChannelCallbacks (const char *channel, Log::Callbacks &log_callbacks); + + bool + RemoveLogChannel (const char *name); +#endif + + std::string + FindLongestCommandWord (CommandObject::CommandMap &dict); + + void + FindCommandsForApropos (const char *word, StringList &commands_found, StringList &commands_help); + + void + AproposAllSubCommands (CommandObject *cmd_obj, const char *prefix, const char *search_word, + StringList &commands_found, StringList &commands_help); + +protected: + friend class Debugger; + + void + SetSynchronous (bool value); + +private: + + lldb::ScriptLanguage m_script_language; + CommandContext m_current_context; + bool m_synchronous_execution; + Listener *m_listener; + SourceManager& m_source_manager; + + 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 + VariableMap m_variables; + OptionArgMap m_alias_options; // Stores any options (with or without arguments) that go with any alias. + std::vector m_command_history; +}; + + +} // namespace lldb_private + +#endif // liblldb_CommandInterpreter_h_ diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h new file mode 100644 index 000000000000..ae050983e343 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandObject.h @@ -0,0 +1,194 @@ +//===-- 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/Core/Args.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Flags.h" + +namespace lldb_private { + +class CommandObject +{ +public: + typedef std::map CommandMap; + + + CommandObject (const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObject (); + + const char * + GetHelp (); + + const char * + GetHelpLong (); + + const char * + GetSyntax (); + + const char * + Translate (); + + const char * + GetCommandName (); + + void + SetHelp (const char * str); + + void + SetHelpLong (const char * str); + + void + SetSyntax (const char *str); + + virtual void + AddObject (const char *obj_name) {} + + virtual bool + IsCrossRefObject () { return false; } + + virtual bool + IsMultiwordObject () { return false; } + + virtual bool + WantsRawCommandString() { return false; } + + virtual Options * + GetOptions (); + + enum + { + eFlagProcessMustBeLaunched = (1 << 0), + eFlagProcessMustBePaused = (1 << 1) + }; + + // Do not override this + bool + ExecuteCommandString (const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + bool + ParseOptions(Args& args, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + bool + ExecuteWithOptions (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + ExecuteRawCommandString (const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + return false; + } + + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) = 0; + + 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. + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + 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. + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches); + + + bool + HelpTextContainsWord (const char *search_word); + + //------------------------------------------------------------------ + /// 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; + +protected: + std::string m_cmd_name; + std::string m_cmd_help_short; + std::string m_cmd_help_long; + std::string m_cmd_syntax; + Flags m_flags; +}; + +} // namespace lldb_private + + +#endif // liblldb_CommandObject_h_ diff --git a/lldb/include/lldb/Interpreter/CommandObjectCrossref.h b/lldb/include/lldb/Interpreter/CommandObjectCrossref.h new file mode 100644 index 000000000000..c1e8e5953630 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandObjectCrossref.h @@ -0,0 +1,60 @@ +//===-- CommandObjectCrossref.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectCrossref_h_ +#define liblldb_CommandObjectCrossref_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Core/Args.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectCrossref +//------------------------------------------------------------------------- + +class CommandObjectCrossref : public CommandObject +{ +public: + CommandObjectCrossref (const char *name, + const char *help = NULL, + const char *syntax = NULL); + + virtual + ~CommandObjectCrossref (); + + void + GenerateHelpText (CommandReturnObject &result); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + IsCrossRefObject (); + + virtual void + AddObject (const char *obj_name); + + const char ** + GetObjectTypes () const; + +private: + Args m_crossref_object_types; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectCrossref_h_ diff --git a/lldb/include/lldb/Interpreter/CommandObjectMultiword.h b/lldb/include/lldb/Interpreter/CommandObjectMultiword.h new file mode 100644 index 000000000000..90f9fd0b8df9 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandObjectMultiword.h @@ -0,0 +1,73 @@ +//===-- 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 +{ +public: + CommandObjectMultiword (const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObjectMultiword (); + + virtual bool + IsMultiwordObject () { return true; } + + bool + LoadSubCommand (lldb::CommandObjectSP command_obj, const char *cmd_name, CommandInterpreter *interpreter); + + void + GenerateHelpText (CommandReturnObject &result, CommandInterpreter *interpreter); + + lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL); + + CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches); + + CommandObject::CommandMap m_subcommand_dict; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectMultiword_h_ diff --git a/lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h b/lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h new file mode 100644 index 000000000000..0c38f5b4fab5 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandObjectRegexCommand.h @@ -0,0 +1,73 @@ +//===-- 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/Interpreter/CommandObject.h" +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectRegexCommand +//------------------------------------------------------------------------- + +class CommandObjectRegexCommand : public CommandObject +{ +public: + + CommandObjectRegexCommand (const char *name, const char *help, const char *syntax, uint32_t max_matches); + + virtual + ~CommandObjectRegexCommand (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + WantsRawCommandString() { return true; } + + virtual bool + ExecuteRawCommandString (const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + + bool + AddRegexCommand (const char *re_cstr, const char *command_cstr); + +protected: + typedef struct Entry + { + RegularExpression regex; + std::string command; + }; + + typedef std::list EntryCollection; + const uint32_t m_max_matches; + EntryCollection m_entries; + +private: + DISALLOW_COPY_AND_ASSIGN (CommandObjectRegexCommand); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectRegexCommand_h_ diff --git a/lldb/include/lldb/Interpreter/CommandReturnObject.h b/lldb/include/lldb/Interpreter/CommandReturnObject.h new file mode 100644 index 000000000000..c95caa260024 --- /dev/null +++ b/lldb/include/lldb/Interpreter/CommandReturnObject.h @@ -0,0 +1,90 @@ +//===-- 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/StreamString.h" + +namespace lldb_private { + + +class CommandReturnObject +{ +public: + + CommandReturnObject (); + + ~CommandReturnObject (); + + StreamString & + GetOutputStream (); + + StreamString & + GetErrorStream (); + + void + Clear(); + + void + AppendMessage (const char *in_string, int len = -1); + + void + AppendMessageWithFormat (const char *format, ...); + + void + AppendRawWarning (const char *in_string, int len = -1); + + void + AppendWarning (const char *in_string, int len = -1); + + void + AppendWarningWithFormat (const char *format, ...); + + void + AppendError (const char *in_string, int len = -1); + + void + AppendRawError (const char *in_string, int len = -1); + + void + AppendErrorWithFormat (const char *format, ...); + + lldb::ReturnStatus + GetStatus(); + + void + SetStatus (lldb::ReturnStatus status); + + bool + Succeeded (); + + bool + HasResult (); + + bool GetDidChangeProcessState (); + + void SetDidChangeProcessState (bool b); + +private: + StreamString m_output_stream; + StreamString m_error_stream; + lldb::ReturnStatus m_status; + bool m_did_change_process_state; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandReturnObject_h_ diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h new file mode 100644 index 000000000000..aad6e30087b4 --- /dev/null +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -0,0 +1,103 @@ +//===-- 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 "PseudoTerminal.h" + +namespace lldb_private { + +class ScriptInterpreter +{ +public: + + typedef enum + { + eCharPtr, + eBool, + eShortInt, + eShortIntUnsigned, + eInt, + eIntUnsigned, + eLongInt, + eLongIntUnsigned, + eLongLong, + eLongLongUnsigned, + eFloat, + eDouble, + eChar + } ReturnType; + + + ScriptInterpreter (lldb::ScriptLanguage script_lang); + + virtual ~ScriptInterpreter (); + + virtual void + ExecuteOneLine (const std::string&, FILE *, FILE *) = 0; + + virtual void + ExecuteInterpreterLoop (FILE *, FILE *) = 0; + + virtual bool + ExecuteOneLineWithReturn (const char *in_string, ReturnType return_type, void *ret_value) + { + return true; + } + + virtual bool + ExecuteMultipleLines (const char *in_string) + { + return true; + } + + virtual bool + ExportFunctionDefinitionToInterpreter (StringList &function_def) + { + return false; + } + + virtual bool + GenerateBreakpointCommandCallbackData (StringList &input, StringList &output) + { + return false; + } + + virtual void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + const char * + GetScriptInterpreterPtyName (); + + int + GetMasterFileDescriptor (); + + CommandInterpreter * + GetCommandInterpreter (); + +private: + lldb::ScriptLanguage m_script_lang; + + // Scripting languages may need to use stdin for their interactive loops; + // however we don't want them to grab the real system stdin because that + // resource needs to be shared among the debugger UI, the inferior process and these + // embedded scripting loops. Therefore we need to set up a pseudoterminal and use that + // as stdin for the script interpreter interactive loops/prompts. + + lldb_utility::PseudoTerminal m_interpreter_pty; + std::string m_pty_slave_name; +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ScriptInterpreter_h_ diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterNone.h b/lldb/include/lldb/Interpreter/ScriptInterpreterNone.h new file mode 100644 index 000000000000..919e17fa956e --- /dev/null +++ b/lldb/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 (); + + ~ScriptInterpreterNone (); + + virtual void + ExecuteOneLine (const std::string &line, FILE *out, FILE *err); + + virtual void + ExecuteInterpreterLoop (FILE *out, FILE *err); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ScriptInterpreterNone_h_ diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h new file mode 100644 index 000000000000..3eceb829a978 --- /dev/null +++ b/lldb/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -0,0 +1,86 @@ +//===-- 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_ + +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Core/InputReader.h" + +#include + +namespace lldb_private { + +class ScriptInterpreterPython : public ScriptInterpreter +{ +public: + + ScriptInterpreterPython (); + + ~ScriptInterpreterPython (); + + void + ExecuteOneLine (const std::string &line, FILE *out, FILE *err); + + void + ExecuteInterpreterLoop (FILE *out, FILE *err); + + bool + ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ReturnType return_type, + void *ret_value); + + bool + ExecuteMultipleLines (const char *in_string); + + bool + ExportFunctionDefinitionToInterpreter (StringList &function_def); + + bool + GenerateBreakpointCommandCallbackData (StringList &input, StringList &output); + + static size_t + GenerateBreakpointOptionsCommandCallback (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); + + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + StringList + ReadCommandInputFromUser (FILE *in_file); + +private: + + static size_t + InputReaderCallback (void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + PyObject *m_compiled_module; + struct termios m_termios; + bool m_termios_valid; +}; + +} // namespace lldb_private + + +#endif // #ifndef liblldb_ScriptInterpreterPython_h_ diff --git a/lldb/include/lldb/Interpreter/StateVariable.h b/lldb/include/lldb/Interpreter/StateVariable.h new file mode 100644 index 000000000000..34be0eedc43a --- /dev/null +++ b/lldb/include/lldb/Interpreter/StateVariable.h @@ -0,0 +1,145 @@ +//===-- StateVariable.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InterpreterStateVariable_h_ +#define liblldb_InterpreterStateVariable_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Args.h" + +namespace lldb_private { + +class StateVariable +{ +public: + + // NOTE: If you add more types to this enumeration list, you need to check for them and "do the right thing" + // in CommandObjectSet::Execute. + typedef enum + { + eTypeBoolean, + eTypeInteger, + eTypeString, + eTypeStringArray + } Type; + + + typedef bool (*Callback) (CommandInterpreter *, + void *, + CommandReturnObject &); + + StateVariable (const char *name, + const char *value, + bool can_append = false, + const char *help_text = "", + Callback func_ptr = NULL); + + StateVariable (const char *name, + bool value, + const char *help_text = "", + Callback func_ptr = NULL); + + StateVariable (const char *name, + int value, + const char *help_text = "", + Callback func_ptr = NULL); + + StateVariable (const char *name, + const Args *value, + const char *help_text = "", + Callback func_ptr = NULL); + + virtual + ~StateVariable (); + + + const char * + GetName () const; + + Type + GetType () const; + + int + GetIntValue () const; + + bool + GetBoolValue () const; + + const char * + GetStringValue () const; + + Args & + GetArgs (); + + const Args & + GetArgs () const; + + const char * + GetHelp () const; + + void + SetHelp (const char *); + + void + AppendVariableInformation (CommandReturnObject &result); + + void + SetStringValue (const char *); + + void + SetIntValue (int); + + void + SetBoolValue (bool); + + void + ArrayAppendValue (const char *); + + void + ArrayClearValues (); + + void + AppendStringValue (const char *new_string); + + bool + VerifyValue (CommandInterpreter *interpreter, + void *data, + CommandReturnObject &result); + + bool + HasVerifyFunction (); + + static bool + VerifyScriptLanguage (CommandInterpreter *interpreter, + void *data, + CommandReturnObject &result); + + static bool + BroadcastPromptChange (CommandInterpreter *interpreter, + void *data, + CommandReturnObject &result); + +private: + std::string m_name; + Type m_type; + int m_int_value; + Args m_string_values; + std::string m_help_text; + Callback m_verification_func_ptr; +}; + + +} // namespace lldb_private + +#endif // liblldb_InterpreterStateVariable_h_ diff --git a/lldb/include/lldb/Symbol/Block.h b/lldb/include/lldb/Symbol/Block.h new file mode 100644 index 000000000000..1e2b23aa2f71 --- /dev/null +++ b/lldb/include/lldb/Symbol/Block.h @@ -0,0 +1,721 @@ +//===-- 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/Stream.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/VMRange.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: + friend class Function; + friend class BlockList; + //------------------------------------------------------------------ + /// Enumeration values for special and invalid Block User ID + /// values. + //------------------------------------------------------------------ + typedef enum + { + RootID = LLDB_INVALID_UID - 1, ///< The Block UID for the root block + InvalidID = LLDB_INVALID_UID ///< Invalid Block UID. + }; + + //------------------------------------------------------------------ + /// 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, uint32_t depth, BlockList* block_list); + + //------------------------------------------------------------------ + /// Copy constructor. + /// + /// Makes a copy of the another Block object \a rhs. + /// + /// @param[in] rhs + /// A const Block object reference to copy. + //------------------------------------------------------------------ + Block (const Block& rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Block (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the block value from another Block object \a rhs + /// into \a this object. + /// + /// @param[in] rhs + /// A const Block object reference to copy. + /// + /// @return + /// A const Block object reference to \a this. + //------------------------------------------------------------------ + const Block& + operator= (const Block& rhs); + + //------------------------------------------------------------------ + /// Add a child to this object. + /// + /// @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. + /// + /// @return + /// Returns \a uid if the child was successfully added to this + /// block, or Block::InvalidID on failure. + //------------------------------------------------------------------ + lldb::user_id_t + AddChild (lldb::user_id_t uid); + + //------------------------------------------------------------------ + /// 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(lldb::addr_t start_offset, lldb::addr_t end_offset); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + //------------------------------------------------------------------ + /// 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 VMRange& range) const; + + bool + ContainsBlockWithID (lldb::user_id_t block_id) 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; + + void + DumpStopContext (Stream *s, const SymbolContext *sc); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + //------------------------------------------------------------------ + /// Get the parent block's UID. + /// + /// @return + /// The UID of the parent block, or Block::InvalidID + /// if this block has no parent. + //------------------------------------------------------------------ + lldb::user_id_t + GetParentUID () const; + + //------------------------------------------------------------------ + /// Get the sibling block's UID. + /// + /// @return + /// The UID of the sibling block, or Block::InvalidID + /// if this block has no sibling. + //------------------------------------------------------------------ + lldb::user_id_t + GetSiblingUID () const; + + //------------------------------------------------------------------ + /// Get the first child block's UID. + /// + /// @return + /// The UID of the first child block, or Block::InvalidID + /// if this block has no first child. + //------------------------------------------------------------------ + lldb::user_id_t + GetFirstChildUID () const; + + //------------------------------------------------------------------ + /// 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. + /// + /// @return + /// A variable list shared pointer that contains all variables + /// for this block. + //------------------------------------------------------------------ + lldb::VariableListSP + GetVariableList (bool get_child_variables, bool can_create); + + + //------------------------------------------------------------------ + /// 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/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, VariableList *variable_list); + + //------------------------------------------------------------------ + /// Get accessor for any inlined function information. + /// + /// @return + /// A pointer to any inlined function information, or NULL if + /// this is a regular block. + //------------------------------------------------------------------ + InlineFunctionInfo* + InlinedFunctionInfo (); + + //------------------------------------------------------------------ + /// Get const accessor for any inlined function information. + /// + /// @return + /// A cpmst pointer to any inlined function information, or NULL + /// if this is a regular block. + //------------------------------------------------------------------ + const InlineFunctionInfo* + InlinedFunctionInfo () const; + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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); + +protected: + //------------------------------------------------------------------ + /// Get accessor for the integer block depth value. + /// + /// @return + /// The integer depth of this block in the block hiearchy. + //------------------------------------------------------------------ + uint32_t Depth () const; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + BlockList *m_block_list; ///< The block list, one of which is this one + uint32_t m_depth; ///< The depth of this block where zero is the root block + VMRange::collection m_ranges; ///< A list of address offset ranges relative to the function's section/offset address. + lldb::InlineFunctionInfoSP m_inlineInfoSP; ///< Inlined function information. + lldb::VariableListSP m_variables; ///< The variable list for all local, static and paramter variables scoped to this block. + // TOOD: add a Type* list +}; + +//---------------------------------------------------------------------- +/// @class BlockList Block.h "lldb/Symbol/Block.h" +/// @brief A class that contains a heirachical collection of lexical +/// block objects where one block is the root. +/// +/// A collection of Block objects is managed by this class. All access +/// to the block data is made through the block_uid of each block. This +/// facilitates partial parsing and can enable block specific data to +/// only be parsed when the data is asked for (variables, params, types, +/// etc). +//---------------------------------------------------------------------- + +class BlockList +{ +public: + friend class Block; + typedef std::vector collection;///< Our block collection type. + + //------------------------------------------------------------------ + /// Construct with \a function and section offset based address + /// range. + /// + /// @param[in] function + /// A const Function object that owns this block list. + /// + /// @param[in] range + /// A section offset based address range object. + //------------------------------------------------------------------ + BlockList (Function *function, const AddressRange& range); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~BlockList (); + + //------------------------------------------------------------------ + /// Add a child block to a parent block. + /// + /// Adds a new child to a parent block. The UID values for + /// blocks are created by the SymbolFile plug-ins and should have + /// values that facilitate correlating an existing Block object + /// with information in the debug information file. Typically + /// a table index, or a debug information offset is used. + /// + /// @param[in] parent_uid + /// The UID for a the existing parent block that will have + /// a new child, whose UID is \a child_uid, added to its + /// child list. + /// + /// @param[in] child_uid + /// The UID for the new child block. + /// + /// @return + /// Returns \a child_uid if the child was successfully added + /// to the parent \a parent_uid, or Block::InvalidID on + /// failure (if the parent doesn't exist). + //------------------------------------------------------------------ + lldb::user_id_t + AddChild (lldb::user_id_t parent_uid, lldb::user_id_t child_uid); + + //------------------------------------------------------------------ + /// Add a child block to a parent block. + /// + /// Adds a new child to a parent block. The UID values for + /// blocks are created by the SymbolFile plug-ins and should have + /// values that facilitate correlating an existing Block object + /// with information in the debug information file. Typically + /// a table index, or a debug information offset is used. + /// + /// @param[in] block_uid + /// The UID for a the existing block that will get the + /// new range. + /// + /// @param[in] start_offset + /// An offset into this object's address range that + /// describes the start address of a range for \a block_uid. + /// + /// @param[in] end_offset + /// An offset into this object's address range that + /// describes the end address of a range for for \a block_uid. + /// + /// @return + /// Returns \b true if the range was successfully added to + /// the block whose UID is \a block_uid, \b false otherwise. + //------------------------------------------------------------------ + bool + AddRange (lldb::user_id_t block_uid, lldb::addr_t start_offset, lldb::addr_t end_offset); + +// const Block * +// FindDeepestBlockForAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Get accessor for the section offset based address range. + /// + /// All Block objects contained in a BlockList are relative to + /// the base address in this object. + /// + /// @return + /// Returns a reference to the section offset based address + /// range object. + //------------------------------------------------------------------ + AddressRange & + GetAddressRange (); + + //------------------------------------------------------------------ + /// Get const accessor for the section offset based address range. + /// + /// All Block objects contained in a BlockList are relative to + /// the base address in this object. + /// + /// @return + /// Returns a const reference to the section offset based + /// address range object. + //------------------------------------------------------------------ + const AddressRange & + GetAddressRange () const; + + //------------------------------------------------------------------ + /// Dump the block list contents. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] block_uid + /// The UID of the block in the block list to dump. If this + /// value is Block::RootID, then the entire block list will + /// dumped as long as \a depth is set to a large enough value. + /// + /// @param[in] depth + /// Limit the number of levels deep that this function should + /// print as the block whose UID is \a block_uid 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::user_id_t block_uid, uint32_t depth, bool show_context) const; + + //------------------------------------------------------------------ + /// Get a block object pointer by block UID. + /// + /// @param[in] block_uid + /// The UID of the block to retrieve. + /// + /// @return + /// A pointer to the block object, or NULL if \a block_uid + /// doesn't exist in the block list. + //------------------------------------------------------------------ + Block * + GetBlockByID (lldb::user_id_t block_uid); + + //------------------------------------------------------------------ + /// Get a const block object pointer by block UID. + /// + /// @param[in] block_uid + /// The UID of the block to retrieve. + /// + /// @return + /// A const pointer to the block object, or NULL if \a block_uid + /// doesn't exist in the block list. + //------------------------------------------------------------------ + const Block * + GetBlockByID (lldb::user_id_t block_uid) const; + + //------------------------------------------------------------------ + /// Get a function object pointer for the block list. + /// + /// @return + /// A pointer to the function object. + //------------------------------------------------------------------ + Function * + GetFunction (); + + //------------------------------------------------------------------ + /// Get a const function object pointer for the block list. + /// + /// @return + /// A const pointer to the function object. + //------------------------------------------------------------------ + const Function * + GetFunction () const; + + //------------------------------------------------------------------ + /// Get the first child block UID for the block whose UID is \a + /// block_uid. + /// + /// @param[in] block_uid + /// The UID of the block we wish to access information for. + /// + /// @return + /// The UID of the first child block, or Block::InvalidID + /// if this block has no children, or if \a block_uid is not + /// a valid block ID for this block list. + //------------------------------------------------------------------ + lldb::user_id_t + GetFirstChild (lldb::user_id_t block_uid) const; + + //------------------------------------------------------------------ + /// Get the parent block UID for the block whose UID is \a + /// block_uid. + /// + /// @param[in] block_uid + /// The UID of the block we wish to access information for. + /// + /// @return + /// The UID of the parent block, or Block::InvalidID + /// if this block has no parent, or if \a block_uid is not + /// a valid block ID for this block list. + //------------------------------------------------------------------ + lldb::user_id_t + GetParent (lldb::user_id_t block_uid) const; + + //------------------------------------------------------------------ + /// Get the sibling block UID for the block whose UID is \a + /// block_uid. + /// + /// @param[in] block_uid + /// The UID of the block we wish to access information for. + /// + /// @return + /// The UID of the sibling block, or Block::InvalidID + /// if this block has no sibling, or if \a block_uid is not + /// a valid block ID for this block list. + //------------------------------------------------------------------ + lldb::user_id_t + GetSibling (lldb::user_id_t block_uid) const; + + //------------------------------------------------------------------ + /// Get the variable list for the block whose UID is \a block_uid. + /// + /// @param[in] block_uid + /// The UID of the block we wish to access information for. + /// + /// @param[in] can_create + /// If \b true, the variable list can be parsed on demand. If + /// \b false, the variable list contained in this object will + /// be returned. + /// + /// @return + /// The variable list shared pointer which may contain a NULL + /// variable list object. + //------------------------------------------------------------------ + lldb::VariableListSP + GetVariableList (lldb::user_id_t block_uid, bool get_child_variables, bool can_create); + + //------------------------------------------------------------------ + /// Check if the block list is empty. + /// + /// @return + /// Returns \b true if the block list is empty, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsEmpty () const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Returns the cost of this object plus any owned objects (address + /// range, and contains Block objects). + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Set the variable list for the block whose UID is \a block_uid. + /// + /// @param[in] block_uid + /// The UID of the block we wish to set information for. + /// + /// @param[in] variable_list_sp + /// A shared pointer to list of variables. + /// + /// @return + /// Returns \b true if the variable list was successfully added + /// to the block, \b false otherwise. + //------------------------------------------------------------------ + bool + SetVariableList (lldb::user_id_t block_uid, lldb::VariableListSP& variable_list_sp); + + //------------------------------------------------------------------ + /// Set the inlined function info for the block whose UID is \a + /// block_uid. + /// + /// @param[in] block_uid + /// The UID of the block we wish to set information for. + /// + /// @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. + /// + /// @return + /// Returns \b true if the inline function info was successfully + /// associated with the block, \b false otherwise. + //------------------------------------------------------------------ + bool + SetInlinedFunctionInfo (lldb::user_id_t block_uid, const char *name, const char *mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr); + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Function *m_function; ///< A pointer to the function that owns this block list. + AddressRange m_range; ///< The section offset based address range. + collection m_blocks; ///< A contiguous array of block objects. + + bool + BlockContainsBlockWithID (const lldb::user_id_t block_id, const lldb::user_id_t find_block_id) const; + +private: + + DISALLOW_COPY_AND_ASSIGN (BlockList); +}; + +} // namespace lldb_private + +#endif // liblldb_Block_h_ diff --git a/lldb/include/lldb/Symbol/ClangASTContext.h b/lldb/include/lldb/Symbol/ClangASTContext.h new file mode 100644 index 000000000000..56e583c12805 --- /dev/null +++ b/lldb/include/lldb/Symbol/ClangASTContext.h @@ -0,0 +1,417 @@ +//===-- 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 +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ClangForward.h" + + +namespace lldb_private { + +class Declaration; + +class ClangASTContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ +// ClangASTContext(Module *module); + + ClangASTContext(const char *target_triple); + +// ClangASTContext(const ConstString &target_triple); + + ~ClangASTContext(); + + clang::ASTContext * + getASTContext(); + + clang::Builtin::Context * + getBuiltinContext(); + + clang::IdentifierTable * + getIdentifierTable(); + + clang::LangOptions * + getLanguageOptions(); + + clang::SelectorTable * + getSelectorTable(); + + clang::SourceManager * + getSourceManager(); + + clang::Diagnostic * + getDiagnostic(); + + clang::TargetOptions * + getTargetOptions(); + + clang::TargetInfo * + getTargetInfo(); + + void + Clear(); + + const char * + GetTargetTriple (); + + void + SetTargetTriple (const char *target_triple); + + //------------------------------------------------------------------ + // Basic Types + //------------------------------------------------------------------ + + void * + GetBuiltinTypeForEncodingAndBitSize (lldb::Encoding encoding, + uint32_t bit_size); + + static void * + GetBuiltinTypeForEncodingAndBitSize (clang::ASTContext *ast_context, + lldb::Encoding encoding, + uint32_t bit_size); + + void * + GetBuiltinTypeForDWARFEncodingAndBitSize ( + const char *type_name, + uint32_t dw_ate, + uint32_t bit_size); + + void * + GetVoidBuiltInType(); + + void * + GetCStringType(bool is_const); + + void * + GetVoidPtrType(bool is_const); + + static void * + GetVoidPtrType(clang::ASTContext *ast_context, bool is_const); + + static void * + CopyType(clang::ASTContext *dest_context, + clang::ASTContext *source_context, + void * clang_type); + + //------------------------------------------------------------------ + // CVR modifiers + //------------------------------------------------------------------ + + static void * + AddConstModifier (void * clang_type); + + static void * + AddRestrictModifier (void * clang_type); + + static void * + AddVolatileModifier (void * clang_type); + + //------------------------------------------------------------------ + // Structure, Unions, Classes + //------------------------------------------------------------------ + + void * + CreateRecordType ( + const char *name, + int kind, + clang::DeclContext *decl_ctx); + + bool + AddFieldToRecordType ( + void * record_qual_type, + const char *name, + void * field_type, + int access, + uint32_t bitfield_bit_size); + + bool + FieldIsBitfield ( + clang::FieldDecl* field, + uint32_t& bitfield_bit_size); + + static bool + FieldIsBitfield ( + clang::ASTContext *ast_context, + clang::FieldDecl* field, + uint32_t& bitfield_bit_size); + + static bool + RecordHasFields (const clang::RecordDecl *record_decl); + + void + SetDefaultAccessForRecordFields ( + void * clang_qual_type, + int default_accessibility, + int *assigned_accessibilities, + size_t num_assigned_accessibilities); + + //------------------------------------------------------------------ + // Aggregate Types + //------------------------------------------------------------------ + static bool + IsAggregateType (void * clang_type); + + static uint32_t + GetNumChildren ( + void * clang_type, + bool omit_empty_base_classes); + + void * + GetChildClangTypeAtIndex ( + const char *parent_name, + void * parent_clang_type, + uint32_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + 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); + + static void * + GetChildClangTypeAtIndex ( + clang::ASTContext *ast_context, + const char *parent_name, + void * parent_clang_type, + uint32_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + 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); + + // Lookup a child given a name. This function will match base class names + // and member member names in "clang_type" only, not descendants. + static uint32_t + GetIndexOfChildWithName (clang::ASTContext *ast_context, + void *clang_type, + const char *name, + bool omit_empty_base_classes); + + // 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. + static size_t + GetIndexOfChildMemberWithName (clang::ASTContext *ast_context, + void *clang_type, + const char *name, + bool omit_empty_base_classes, + std::vector& child_indexes); + + //------------------------------------------------------------------ + // clang::TagType + //------------------------------------------------------------------ + + bool + SetTagTypeKind ( + void * tag_qual_type, + int kind); + + //------------------------------------------------------------------ + // C++ Base Classes + //------------------------------------------------------------------ + + clang::CXXBaseSpecifier * + CreateBaseClassSpecifier ( + void * base_class_type, + int access, + bool is_virtual, + bool base_of_class); + + bool + SetBaseClassesForClassType ( + void * class_clang_type, + clang::CXXBaseSpecifier const * const *base_classes, + unsigned num_base_classes); + + //------------------------------------------------------------------ + // DeclContext Functions + //------------------------------------------------------------------ + + static clang::DeclContext * + GetDeclContextForType (void * qual_type); + + //------------------------------------------------------------------ + // Namespace Declarations + //------------------------------------------------------------------ + + clang::NamespaceDecl * + GetUniqueNamespaceDeclaration ( + const char *name, + const Declaration &decl, + clang::DeclContext *decl_ctx); + + //------------------------------------------------------------------ + // Function Types + //------------------------------------------------------------------ + + clang::FunctionDecl * + CreateFunctionDeclaration ( + const char *name, + void * function_Type, + int storage, + bool is_inline); + + void * + CreateFunctionType ( + void * result_type, + void **args, + unsigned num_args, + bool isVariadic, + unsigned TypeQuals); + + clang::ParmVarDecl * + CreateParmeterDeclaration ( + const char *name, + void * return_type, + int storage); + + void + SetFunctionParameters ( + clang::FunctionDecl *function_decl, + clang::ParmVarDecl **params, + unsigned num_params); + + //------------------------------------------------------------------ + // Array Types + //------------------------------------------------------------------ + + void * + CreateArrayType ( + void * element_type, + size_t element_count, + uint32_t bit_stride); + + //------------------------------------------------------------------ + // Tag Declarations + //------------------------------------------------------------------ + bool + StartTagDeclarationDefinition (void * qual_type); + + bool + CompleteTagDeclarationDefinition (void * qual_type); + + //------------------------------------------------------------------ + // Enumeration Types + //------------------------------------------------------------------ + void * + CreateEnumerationType (const Declaration &decl, const char *name); + + bool + AddEnumerationValueToEnumerationType ( + void * enum_qual_type, + void * enumerator_qual_type, + const Declaration &decl, + const char *name, + int64_t enum_value, + uint32_t enum_value_bit_size); + + //------------------------------------------------------------------ + // Pointers & References + //------------------------------------------------------------------ + void * + CreatePointerType (void * clang_type); + + void * + CreateLValueReferenceType (void * clang_type); + + void * + CreateRValueReferenceType (void * clang_type); + + size_t + GetPointerBitSize (); + + static size_t + GetTypeBitSize (clang::ASTContext *ast_context, void * clang_type); + + static size_t + GetTypeBitAlign (clang::ASTContext *ast_context, void * clang_type); + + static bool + IsIntegerType (void * clang_type, bool &is_signed); + + static bool + IsPointerType (void * clang_type, void **target_type = NULL); + + static bool + IsPointerOrReferenceType (void * clang_type, void **target_type = NULL); + + static bool + IsCStringType (void * clang_type, uint32_t &length); + + static bool + IsArrayType (void * clang_type, void **member_type = NULL, uint64_t *size = NULL); + + //------------------------------------------------------------------ + // Typedefs + //------------------------------------------------------------------ + void * + CreateTypedefType ( + const char *name, + void * clang_type, + clang::DeclContext *decl_ctx); + + //------------------------------------------------------------------ + // Type names + //------------------------------------------------------------------ + static std::string + GetTypeName(void *clang_type); + + static bool + IsFloatingPointType (void * clang_type, uint32_t &count, bool &is_complex); + + //static bool + //ConvertFloatValueToString (clang::ASTContext *ast_context, void * clang_type, const uint8_t* bytes, size_t byte_size, int apint_byte_order, std::string &float_str); + + static size_t + ConvertStringToFloatValue (clang::ASTContext *ast_context, void * clang_type, const char *s, uint8_t *dst, size_t dst_size); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangASTContext can see and modify these + //------------------------------------------------------------------ + std::string m_target_triple; + std::auto_ptr m_ast_context_ap; + std::auto_ptr m_language_options_ap; + std::auto_ptr m_source_manager_ap; + std::auto_ptr m_diagnostic_ap; + std::auto_ptr m_target_options_ap; + std::auto_ptr m_target_info_ap; + std::auto_ptr m_identifier_table_ap; + std::auto_ptr m_selector_table_ap; + std::auto_ptr m_builtins_ap; + +private: + //------------------------------------------------------------------ + // For ClangASTContext only + //------------------------------------------------------------------ + ClangASTContext(const ClangASTContext&); + const ClangASTContext& operator=(const ClangASTContext&); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangASTContext_h_ diff --git a/lldb/include/lldb/Symbol/CompileUnit.h b/lldb/include/lldb/Symbol/CompileUnit.h new file mode 100644 index 000000000000..15c3714a7315 --- /dev/null +++ b/lldb/include/lldb/Symbol/CompileUnit.h @@ -0,0 +1,395 @@ +//===-- 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/Symbol/Function.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Language.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 ModuleChild, + public FileSpec, + public UserID, + public Language, + 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 Language::Type + //------------------------------------------------------------------ + CompileUnit(Module *module, void *user_data, const char *pathname, lldb::user_id_t uid, Language::Type 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 Language::Type + //------------------------------------------------------------------ + CompileUnit(Module *module, void *user_data, const FileSpec &file_spec, lldb::user_id_t uid, Language::Type language); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~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); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + //------------------------------------------------------------------ + /// 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[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, + 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. + 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::auto_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 = (1 << 0), ///< Have we already parsed all our functions + flagsParsedVariables = (1 << 1), ///< Have we already parsed globals and statics? + flagsParsedSupportFiles = (1 << 2), ///< Have we already parsed the support files for this compile unit? + flagsParsedLineTable = (1 << 3), ///< Have we parsed the line table already? + }; + + DISALLOW_COPY_AND_ASSIGN (CompileUnit); +}; + +} // namespace lldb_private + +#endif // liblldb_CompUnit_h_ diff --git a/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h b/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h new file mode 100644 index 000000000000..08dc42e2d1d8 --- /dev/null +++ b/lldb/include/lldb/Symbol/DWARFCallFrameInfo.h @@ -0,0 +1,312 @@ +//===-- 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_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/VMRange.h" +#include "lldb/Core/dwarf.h" + +namespace lldb_private { +//---------------------------------------------------------------------- +// DWARFCallFrameInfo +// +// State that describes all register locations for a given address +// range. +//---------------------------------------------------------------------- + +class DWARFCallFrameInfo +{ +public: + enum + { + CFI_AUG_MAX_SIZE = 8, + CFI_HEADER_SIZE = 8 + }; + + class Row; + + class RegisterLocation + { + public: + + typedef enum Type + { + 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... + isUndefined, // reg is not available + isSame, // 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(); + + bool + operator == (const RegisterLocation& rhs) const; + + void + Dump(Stream *s, const DWARFCallFrameInfo &cfi, Thread *thread, const Row *row, uint32_t reg_num) const; + + void + SetUnspecified(); + + void + SetUndefined(); + + void + SetSame() ; + + void + SetAtCFAPlusOffset (int64_t offset); + + void + SetIsCFAPlusOffset (int64_t offset); + + void + SetInRegister (uint32_t reg_num); + + void + SetAtDWARFExpression (const uint8_t *opcodes, uint32_t len); + + void + SetIsDWARFExpression (const uint8_t *opcodes, uint32_t len); + + protected: + Type 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; + uint32_t length; + } expr; + } m_location; + }; + + class Row + { + public: + + Row (); + + ~Row (); + + void + Clear(); + + void + Dump(Stream* s, const DWARFCallFrameInfo &cfi, Thread *thread, lldb::addr_t base_addr) 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 slide) + { + m_offset += slide; + } + + uint32_t + GetCFARegister () const + { + return m_cfa_reg_num; + } + + void + SetCFARegister (uint32_t reg_num) + { + m_cfa_reg_num = reg_num; + } + + int32_t + GetCFAOffset () const + { + return m_cfa_offset; + } + + void + SetCFAOffset (int32_t offset) + { + m_cfa_offset = offset; + } + + protected: + typedef std::map collection; + lldb::addr_t m_offset; // The an offset into the DBAddressRange that owns 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; + }; + + //------------------------------------------------------------------ + // Common Information Entry (CIE) + //------------------------------------------------------------------ +protected: + + struct CIE + { + typedef lldb::SharedPtr::Type shared_ptr; + dw_offset_t cie_offset; + uint8_t version; + char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very short. If we ever run into the limit, make this a NSData pointer + 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; + + CIE(dw_offset_t offset); + ~CIE(); + + void + Dump(Stream *s, Thread* threadState, const ArchSpec *arch, uint32_t reg_kind) const; + }; + + //------------------------------------------------------------------ + // Frame Description Entry (FDE) + //------------------------------------------------------------------ +public: + + class FDE + { + public: + typedef lldb::SharedPtr::Type shared_ptr; + + FDE (uint32_t offset, const AddressRange &range); + ~FDE(); + + const AddressRange & + GetAddressRange() const; + + void + AppendRow (const Row &row); + + bool + IsValidRowIndex (uint32_t idx) const; + + void + Dump (Stream *s, const DWARFCallFrameInfo &cfi, Thread* thread) const; + + const Row& + GetRowAtIndex (uint32_t idx); + + protected: + typedef std::vector collection; + uint32_t m_fde_offset; + AddressRange m_range; + collection m_row_list; + private: + DISALLOW_COPY_AND_ASSIGN (FDE); + }; + + DWARFCallFrameInfo(ObjectFile *objfile, lldb_private::Section *section, uint32_t reg_kind); + + ~DWARFCallFrameInfo(); + + bool + IsEHFrame() const; + + const ArchSpec * + GetArchitecture() const; + + uint32_t + GetRegisterKind () const; + + void + SetRegisterKind (uint32_t reg_kind); + + void + Index (); + +// bool UnwindRegister (const uint32_t reg_num, const Thread* currState, const Row* row, Thread* unwindState); +// uint32_t UnwindThreadState(const Thread* curr_state, bool is_first_frame, Thread* unwound_state); + const FDE * + FindFDE(const Address &addr); + + void + Dump(Stream *s, Thread *thread) const; + + void + ParseAll(); +protected: + + enum + { + eFlagParsedIndex = (1 << 0) + }; + + typedef std::map cie_map_t; + struct FDEInfo + { + off_t fde_offset; + FDE::shared_ptr fde_sp; + FDEInfo (off_t offset); + FDEInfo (); + + }; + typedef std::map fde_map_t; + + ObjectFile * m_objfile; + lldb_private::Section * m_section; + uint32_t m_reg_kind; + Flags m_flags; + DataExtractor m_cfi_data; + cie_map_t m_cie_map; + fde_map_t m_fde_map; + + const CIE* + GetCIE (uint32_t offset); + + void + ParseInstructions(const CIE *cie, FDE *fde, uint32_t instr_offset, uint32_t instr_length); + + CIE::shared_ptr + ParseCIE (const uint32_t cie_offset); + + FDE::shared_ptr + ParseFDE (const uint32_t fde_offset); +}; + +} // namespace lldb_private + +#endif // liblldb_DWARFCallFrameInfo_h_ diff --git a/lldb/include/lldb/Symbol/Declaration.h b/lldb/include/lldb/Symbol/Declaration.h new file mode 100644 index 000000000000..ba0c68d43e65 --- /dev/null +++ b/lldb/include/lldb/Symbol/Declaration.h @@ -0,0 +1,204 @@ +//===-- 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/Core/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 (); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Construct with a reference to another Declaration object. + //------------------------------------------------------------------ + Declaration (const Declaration& rhs); + + //------------------------------------------------------------------ + /// Construct with a pointer to another Declaration object. + //------------------------------------------------------------------ + Declaration (const Declaration* rhs_ptr); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the file specification to be empty, and the line and column + /// to zero. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// 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) const; + + void + DumpStopContext (Stream *s) 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; + + //------------------------------------------------------------------ + /// Get accessor for file specification. + /// + /// @return + /// A reference to the file specification object. + //------------------------------------------------------------------ + FileSpec& + GetFile (); + + //------------------------------------------------------------------ + /// Get const accessor for file specification. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec& + GetFile () const; + + //------------------------------------------------------------------ + /// 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; + + + bool + IsValid() 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 () + //------------------------------------------------------------------ + 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); + + //------------------------------------------------------------------ + /// Set accessor for the declaration file specification. + /// + /// @param[in] file_spec + /// The new declaration file specifciation. + //------------------------------------------------------------------ + void + SetFile (const FileSpec& 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); +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. + uint32_t m_column; ///< Non-zero values indicates a valid column number, + ///< zero indicates no column information is available. +}; + +} // namespace lldb_private + +#endif // liblldb_Declaration_h_ diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h new file mode 100644 index 000000000000..74029da1e061 --- /dev/null +++ b/lldb/include/lldb/Symbol/Function.h @@ -0,0 +1,587 @@ +//===-- 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/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) 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) const; + + void + DumpStopContext (Stream *s) 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); + + const AddressRange & + GetAddressRange(); + + //------------------------------------------------------------------ + /// Get accessor for the block list. + /// + /// @return + /// The block list object that describes all lexical blocks + /// in the function. + /// + /// @see BlockList + //------------------------------------------------------------------ + BlockList& + GetBlocks (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; + + //------------------------------------------------------------------ + /// 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 Mangled & + GetMangled() const + { + return m_mangled; + } + + //------------------------------------------------------------------ + /// 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; + + Type + GetReturnType (); + + // The Number of arguments, or -1 for an unprototyped function. + int + GetArgumentCount (); + + const Type + GetArgumentTypeAtIndex (size_t idx); + + const char * + GetArgumentNameAtIndex (size_t idx); + + bool + IsVariadic (); + + 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. + BlockList m_blocks; ///< All lexical blocks contained in this function. + 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/lldb/include/lldb/Symbol/LineEntry.h b/lldb/include/lldb/Symbol/LineEntry.h new file mode 100644 index 000000000000..e0e6d2941d19 --- /dev/null +++ b/lldb/include/lldb/Symbol/LineEntry.h @@ -0,0 +1,170 @@ +//===-- 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/Core/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 + ( + Section *section, + 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, Process *process, bool show_file, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_range) const; + + bool + GetDescription (Stream *s, lldb::DescriptionLevel level, CompileUnit* cu, Process *process) 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) 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/lldb/include/lldb/Symbol/LineTable.h b/lldb/include/lldb/Symbol/LineTable.h new file mode 100644 index 000000000000..2329eea37209 --- /dev/null +++ b/lldb/include/lldb/Symbol/LineTable.h @@ -0,0 +1,333 @@ +//===-- 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" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @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 guarantee the addresses are in increasing order + void + AppendLineEntry (lldb::SectionSP& section_sp, + lldb::addr_t section_offset, + 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); + + // Called when you can't guarantee the addresses are in increasing order + void + InsertLineEntry (lldb::SectionSP& section_sp, + lldb::addr_t section_offset, + 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); + + //------------------------------------------------------------------ + /// 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, Process *process, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_line_ranges); + + void + GetDescription (Stream *s, Process *process, 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); + + //------------------------------------------------------------------ + /// 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; + +protected: + + struct Entry + { + enum { kInvalidSectIdx = 255 }; + + Entry () : + sect_idx (kInvalidSectIdx), + sect_offset (0), + 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 ( uint8_t _sect_idx, + lldb::addr_t _sect_offset, + 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) : + sect_idx (_sect_idx), + sect_offset (_sect_offset), + 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) + { + // We have reserved 24 bits for the section offset which should + // be enough, but if it isn't then we need to make m_section_offset + // bigger + assert((_sect_offset & 0xffffffffff000000ull) == 0); + } + + int + bsearch_compare (const void *key, const void *arrmem); + + void + Clear () + { + sect_idx = kInvalidSectIdx; + sect_offset = 0; + 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.sect_offset, rhs.sect_offset); + 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& a, const LineTable::Entry& a) const; + protected: + LineTable *m_line_table; + }; + + static bool EntryAddressLessThan (const Entry& lhs, const Entry& rhs) + { + if (lhs.sect_idx == rhs.sect_idx) + return lhs.sect_offset < rhs.sect_offset; + return lhs.sect_idx < rhs.sect_idx; + } + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + uint32_t sect_idx:8, ///< The section index for this line entry. + sect_offset:24; ///< The offset into the section 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 line entries. + 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. + SectionList m_section_list; ///< The list of sections that at least one of the line entries exists in. + entry_collection m_entries; ///< The collection of line entries in this line table. + + bool + ConvertEntryAtIndexToLineEntry (uint32_t idx, LineEntry &line_entry); + + lldb_private::Section * + GetSectionForEntryIndex (uint32_t idx); +private: + DISALLOW_COPY_AND_ASSIGN (LineTable); +}; + +} // namespace lldb_private + +#endif // liblldb_LineTable_h_ diff --git a/lldb/include/lldb/Symbol/ObjectContainer.h b/lldb/include/lldb/Symbol/ObjectContainer.h new file mode 100644 index 000000000000..f5c6d0780f8c --- /dev/null +++ b/lldb/include/lldb/Symbol/ObjectContainer.h @@ -0,0 +1,232 @@ +//===-- 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/Core/FileSpec.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.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 (Module* module, + const FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length, + lldb::DataBufferSP& headerDataSP) : + ModuleChild (module), + m_file (), // This file can be different than the module's file spec + m_offset (offset), + m_length (length), + m_data (headerDataSP, lldb::eByteOrderHost, 4) + { + if (file) + m_file = *file; + } + + //------------------------------------------------------------------ + /// 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 ObjectFile * + 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/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h new file mode 100644 index 000000000000..f9e5c2da111b --- /dev/null +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -0,0 +1,309 @@ +//===-- 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/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/Symtab.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). +/// +/// It is possible to determine how many architectures an object file +/// contains by using the ObjectFile::GetNumArchitectures() const +/// accessor function. The individual architectures can be extracted +/// using the +/// ObjectFile::GetArchitectureAtIndex (uint32_t, ArchSpec&) const +/// function. The object file can also be asked to select one of these +/// architectures using the +/// ObjectFile::SetArchitecture (const ArchSpec&) function. +/// +/// 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 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. + //------------------------------------------------------------------ + ObjectFile (Module* module, const FileSpec *file_spec_ptr, lldb::addr_t offset, lldb::addr_t length, lldb::DataBufferSP& headerDataSP) : + ModuleChild (module), + m_file (), // This file could be different from the original module's file + m_offset (offset), + m_length (length), + m_data (headerDataSP, lldb::eByteOrderHost, 4) + { + if (file_spec_ptr) + m_file = *file_spec_ptr; + } + + //------------------------------------------------------------------ + /// 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 ObjectFile* + FindPlugin (Module* module, + const FileSpec* file_spec, + lldb::addr_t file_offset, + lldb::addr_t file_size); + + //------------------------------------------------------------------ + /// 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 size_t + GetAddressByteSize () const = 0; + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// 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 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 + GetTargetTriple(ConstString &target_triple) = 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 () = 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; + + //------------------------------------------------------------------ + /// 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 (UUID* uuid) = 0; + + //------------------------------------------------------------------ + /// Gets wether 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; + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + FileSpec m_file; + lldb::addr_t m_offset; ///< The offset in bytes into the file, or the address in memory + lldb::addr_t m_length; ///< The length of this object file if it is known (can be zero if length is unknown or can't be determined). + DataExtractor m_data; ///< The data for this object file so things can be parsed lazily. + +private: + DISALLOW_COPY_AND_ASSIGN (ObjectFile); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjectFile_h_ + diff --git a/lldb/include/lldb/Symbol/Symbol.h b/lldb/include/lldb/Symbol/Symbol.h new file mode 100644 index 000000000000..ec5596fe0b07 --- /dev/null +++ b/lldb/include/lldb/Symbol/Symbol.h @@ -0,0 +1,181 @@ +//===-- 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" + +namespace lldb_private { + +class Symbol : + public UserID // Used to uniquely identify this symbol in its symbol table +{ +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 (lldb::user_id_t symID, + const char *name, + bool name_is_mangled, + lldb::SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const Section* section, + lldb::addr_t value, + uint32_t size, + uint32_t flags); + + Symbol (lldb::user_id_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, + uint32_t flags); + + Symbol (const Symbol& rhs); + + const Symbol& + operator= (const Symbol& rhs); + + bool + Compare (const ConstString& name, lldb::SymbolType type) const; + + void + Dump (Stream *s, Process *process, uint32_t index) const; + + AddressRange * + GetAddressRangePtr (); + + const AddressRange * + GetAddressRangePtr () const; + + AddressRange & + GetAddressRangeRef(); + + const AddressRange & + GetAddressRangeRef() const; + + Mangled& + GetMangled (); + + const Mangled& + GetMangled () const; + + bool + GetSizeIsSibling () const; + + bool + GetSizeIsSynthesized() const; + + uint32_t + GetSiblingIndex () const; + + uint32_t + GetByteSize () const; + + lldb::SymbolType + GetType () const; + + void + SetType (lldb::SymbolType type); + + const char * + GetTypeAsString () const; + + uint32_t + GetFlags () const; + + void + SetFlags (uint32_t flags); + + Function * + GetFunction (); + + Address & + GetValue (); + + const Address & + GetValue () const; + + bool + IsSynthetic () const; + + void + SetIsSynthetic (bool b); + + void + SetSizeIsSynthesized(bool b); + + bool + IsDebug () const; + + void + SetDebug (bool b); + + bool + IsExternal () const; + + void + SetExternal (bool b); + + bool + IsTrampoline () const; + + void + SetByteSize (uint32_t size); + + void + SetSizeIsSibling (bool b); + + void + SetValue (Address &value); + + void + SetValue (const AddressRange &range); + + void + SetValue (lldb::addr_t 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 (); + +protected: + + Mangled m_mangled; // uniqued symbol name/mangled name pair + lldb::SymbolType m_type; // symbol type + 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_searched_for_function:1;// non-zero if we have looked for the function associated with this symbol already. + 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 + Function * m_function; +}; + +} // namespace lldb_private + +#endif // liblldb_Symbol_h_ diff --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h new file mode 100644 index 000000000000..c0aff6ea4a74 --- /dev/null +++ b/lldb/include/lldb/Symbol/SymbolContext.h @@ -0,0 +1,328 @@ +//===-- 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/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); + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// 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, Process *process) 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 ouput. 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. + //------------------------------------------------------------------ + void + DumpStopContext (Stream *s, + ExecutionContextScope *exe_scope, + const Address &so_addr, + bool show_module = true) 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 + /// - function address range if function is not NULL + /// - symbol address range if symbol is not NULL + /// + /// @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, AddressRange &range) const; + + //------------------------------------------------------------------ + /// Find a function matching the given name, working out from this + /// symbol context. + /// + /// @return + /// A shared pointer to the function found. + //------------------------------------------------------------------ + Function * + FindFunctionByName (const char *name) const; + + //------------------------------------------------------------------ + /// Find a variable matching the given name, working out from this + /// symbol context. + /// + /// @return + /// A shared pointer to the variable found. + //------------------------------------------------------------------ + lldb::VariableSP + FindVariableByName (const char *name) const; + + //------------------------------------------------------------------ + /// Find a type matching the given name, working out from this + /// symbol context. + /// + /// @return + /// A shared pointer to the variable found. + //------------------------------------------------------------------ + lldb::TypeSP + FindTypeByName (const char *name) 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 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); + + //------------------------------------------------------------------ + /// 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, Process *process) 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(uint32_t idx, SymbolContext& sc) const; + + bool + RemoveContextAtIndex (uint32_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; + +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); + +} // namespace lldb_private + +#endif // liblldb_SymbolContext_h_ diff --git a/lldb/include/lldb/Symbol/SymbolContextScope.h b/lldb/include/lldb/Symbol/SymbolContextScope.h new file mode 100644 index 000000000000..9b9b9d698ae3 --- /dev/null +++ b/lldb/include/lldb/Symbol/SymbolContextScope.h @@ -0,0 +1,79 @@ +//===-- 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 can reconstruct its symbol +/// context. +/// +/// Many objects that have pointers back to parent objects that own them +/// that all inherit from this pure virtual class can reconstruct their +/// symbol context without having to keep a complete SymbolContextScope +/// object in the object state. Examples of these objects include: +/// Module, CompileUnit, Function, and Block. +/// +/// Other objects can contain a valid pointer to an instance of this +/// class so they can reconstruct the symbol context in which they are +/// scoped. Example objects include: Variable and Type. Such objects +/// can be scoped at a variety of levels: +/// @li module level for a built built in types. +/// @li file level for compile unit types and variables. +/// @li function or block level for types and variables defined in +/// a function body. +/// +/// 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: + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// 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/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h new file mode 100644 index 000000000000..4af88c31b06b --- /dev/null +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -0,0 +1,95 @@ +//===-- 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/Type.h" + +namespace lldb_private { + +class SymbolFile : + public PluginInterface +{ +public: + enum Abilities + { + Labels = (1 << 0), + AddressAcceleratorTable = (1 << 1), + FunctionAcceleratorTable = (1 << 2), + TypeAcceleratorTable = (1 << 3), + MacroInformation = (1 << 4), + CallFrameInformation = (1 << 5), + CompileUnits = (1 << 6), + LineTables = (1 << 7), + LineColumns = (1 << 8), + Functions = (1 << 9), + Blocks = (1 << 10), + GlobalVariables = (1 << 11), + LocalVariables = (1 << 12), + VariableTypes = (1 << 13) + }; + + static SymbolFile * + FindPlugin (ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFile(ObjectFile* obj_file) : + m_obj_file(obj_file) + { + } + + virtual + ~SymbolFile() + { + } + + virtual uint32_t GetAbilities () = 0; + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + // Approach 1 - iterator + virtual uint32_t GetNumCompileUnits() = 0; + virtual lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) = 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 clang::DeclContext* GetClangDeclContextForTypeUID (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, 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, bool append, SymbolContextList& sc_list) = 0; + virtual uint32_t FindFunctions (const RegularExpression& regex, bool append, SymbolContextList& sc_list) = 0; +// virtual uint32_t FindTypes (const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) = 0; +// virtual uint32_t FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) = 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. + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolFile); +}; + + +} // namespace lldb_private + +#endif // liblldb_SymbolFile_h_ diff --git a/lldb/include/lldb/Symbol/SymbolVendor.h b/lldb/include/lldb/Symbol/SymbolVendor.h new file mode 100644 index 000000000000..a5bdb3b30b10 --- /dev/null +++ b/lldb/include/lldb/Symbol/SymbolVendor.h @@ -0,0 +1,193 @@ +//===-- 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/Host/Mutex.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 bool + RegisterPlugin (const char *name, + const char *description, + SymbolVendorCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolVendorCreateInstance create_callback); + + + static SymbolVendor* + FindPlugin (Module* module); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendor(Module *module); + + virtual + ~SymbolVendor(); + + void + AddSymbolFileRepresendation(ObjectFile *obj_file); + + virtual void + Dump(Stream *s); + + 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 uint32_t + FindGlobalVariables(const ConstString &name, + bool append, + uint32_t max_matches, + VariableList& variables); + + virtual uint32_t + FindGlobalVariables(const RegularExpression& regex, + bool append, + uint32_t max_matches, + VariableList& variables); + + virtual uint32_t + FindFunctions(const ConstString &name, + bool append, + SymbolContextList& sc_list); + + virtual uint32_t + FindFunctions(const RegularExpression& regex, + bool append, + SymbolContextList& sc_list); + + virtual uint32_t + GetNumCompileUnits(); + + virtual bool + SetCompileUnitAtIndex (lldb::CompUnitSP& cu, + uint32_t index); + + virtual lldb::CompUnitSP + GetCompileUnitAtIndex(uint32_t idx); + + TypeList& + GetTypeList() + { + return m_type_list; + } + + const TypeList& + GetTypeList() const + { + return m_type_list; + } + + SymbolFile * + GetSymbolFile() + { + return m_sym_file_ap.get(); + } + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, Stream *strm); + + virtual Error + ExecutePluginCommand (Args &command, Stream *strm); + + virtual Log * + EnablePluginLogging (Stream *strm, Args &command); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from SymbolVendor can see and modify these + //------------------------------------------------------------------ + typedef std::vector CompileUnits; + typedef CompileUnits::iterator CompileUnitIter; + typedef CompileUnits::const_iterator CompileUnitConstIter; + + mutable Mutex m_mutex; + TypeList m_type_list; // Uniqued types for all parsers owned by this module + CompileUnits m_compile_units; // The current compile units + std::auto_ptr m_sym_file_ap; // A single symbol file. Suclasses 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/lldb/include/lldb/Symbol/Symtab.h b/lldb/include/lldb/Symbol/Symtab.h new file mode 100644 index 000000000000..cb98db92713c --- /dev/null +++ b/lldb/include/lldb/Symbol/Symtab.h @@ -0,0 +1,77 @@ +//===-- 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/UniqueCStringMap.h" +#include "lldb/Symbol/Symbol.h" + +namespace lldb_private { + +class Symtab +{ +public: + Symtab(ObjectFile *objfile); + ~Symtab(); + + void Reserve (uint32_t count); + Symbol * Resize (uint32_t count); + uint32_t AddSymbol(const Symbol& symbol); + size_t GetNumSymbols() const; + void Dump(Stream *s, Process *process) const; + void Dump(Stream *s, Process *process, std::vector& indexes) const; + + Symbol * SymbolAtIndex (uint32_t idx); + const Symbol * SymbolAtIndex (uint32_t idx) const; + Symbol * FindSymbolWithType (lldb::SymbolType symbol_type, uint32_t &start_idx); + const Symbol * FindSymbolWithType (lldb::SymbolType symbol_type, uint32_t &start_idx) const; + uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, std::vector& matches, uint32_t start_idx = 0, uint32_t end_index = UINT_MAX) const; + uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, std::vector& matches); + uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, std::vector& matches); + uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, std::vector& indexes); + size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, std::vector& symbol_indexes); + size_t FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, std::vector& symbol_indexes); + Symbol * FindFirstSymbolWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type = lldb::eSymbolTypeAny); + Symbol * FindSymbolWithFileAddress (lldb::addr_t file_addr); +// Symbol * FindSymbolContainingAddress (const Address& value, const uint32_t* indexes, uint32_t num_indexes); +// Symbol * FindSymbolContainingAddress (const Address& value); + Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes); + Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr); + size_t CalculateSymbolSize (Symbol *symbol); + + void SortSymbolIndexesByValue (std::vector& indexes, bool remove_duplicates) const; + + static void DumpSymbolHeader (Stream *s); + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + static int CompareSymbolValueByIndex (void *thunk, const void *a, const void *b); + void InitNameIndexes (); + void InitAddressIndexes (); + + ObjectFile * m_objfile; + collection m_symbols; + std::vector m_addr_indexes; + UniqueCStringMap m_name_to_index; + +private: + DISALLOW_COPY_AND_ASSIGN (Symtab); +}; + +} // namespace lldb_private + +#endif // liblldb_Symtab_h_ diff --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h new file mode 100644 index 000000000000..9d7810af9c04 --- /dev/null +++ b/lldb/include/lldb/Symbol/Type.h @@ -0,0 +1,286 @@ +//===-- 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/UserID.h" +#include "lldb/Symbol/Declaration.h" +#include + +namespace lldb_private { + +class Type : public UserID +{ +public: + typedef enum + { + eTypeInvalid, + eIsTypeWithUID, ///< This type is the type whose UID is m_encoding_uid + eIsConstTypeWithUID, ///< This type is the type whose UID is m_encoding_uid with the const qualifier added + eIsRestrictTypeWithUID, ///< This type is the type whose UID is m_encoding_uid with the restrict qualifier added + eIsVolatileTypeWithUID, ///< This type is the type whose UID is m_encoding_uid with the volatile qualifier added + eTypedefToTypeWithUID, ///< This type is pointer to a type whose UID is m_encoding_uid + ePointerToTypeWithUID, ///< This type is pointer to a type whose UID is m_encoding_uid + eLValueReferenceToTypeWithUID, ///< This type is L value reference to a type whose UID is m_encoding_uid + eRValueReferenceToTypeWithUID, ///< This type is R value reference to a type whose UID is m_encoding_uid + eTypeUIDSynthetic + } EncodingUIDType; + + Type(lldb::user_id_t uid, + SymbolFile* symbol_file, + const ConstString &name, + uint64_t byte_size, + SymbolContextScope *context, + lldb::user_id_t encoding_uid, + EncodingUIDType encoding_type, + const Declaration& decl, + void *clang_qual_type); + + // This makes an invalid type. Used for functions that return a Type when they + // get an error. + Type(); + + const Type& + operator= (const Type& rhs); + + void + Dump(Stream *s, bool show_context); + + void + DumpTypeName(Stream *s); + + 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 != eTypeInvalid; + } + + void + SetByteSize(uint32_t byte_size); + + const ConstString & + GetName () const + { + return m_name; + } + + static ConstString + GetClangTypeName (void *clang_qual_type); + + static void + DumpValue(ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_qual_type, + Stream *s, + lldb::Format format, + const DataExtractor &data, + uint32_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); + + static void + DumpSummary (ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_qual_type, + Stream *s, + const DataExtractor &data, + uint32_t data_offset, + size_t data_byte_size); + + + 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, + lldb::AddressType address_type, + bool show_types, + bool show_summary, + bool verbose); + + static bool + DumpTypeValue ( Stream *s, + clang::ASTContext *ast_context, + void *clang_qual_type, + lldb::Format format, + const DataExtractor &data, + uint32_t data_offset, + size_t data_byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset); + + static bool + GetValueAsScalar (clang::ASTContext *ast_context, + void *clang_qual_type, + const DataExtractor &data, + uint32_t data_offset, + size_t data_byte_size, + Scalar &value); + + static bool + SetValueFromScalar (clang::ASTContext *ast_context, + void *clang_qual_type, + const Scalar &value, + Stream &strm); + + bool + ReadFromMemory (ExecutionContext *exe_ctx, + lldb::addr_t address, + lldb::AddressType address_type, + DataExtractor &data); + + bool + WriteToMemory (ExecutionContext *exe_ctx, + lldb::addr_t address, + lldb::AddressType address_type, + DataExtractor &data); + + + static bool + ReadFromMemory (ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_qual_type, + lldb::addr_t addr, + lldb::AddressType address_type, + DataExtractor &data); + + static bool + WriteToMemory (ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_qual_type, + lldb::addr_t addr, + lldb::AddressType address_type, + StreamString &new_value); + + bool + GetIsDeclaration() const; + + void + SetIsDeclaration(bool b); + + bool + GetIsExternal() const; + + void + SetIsExternal(bool b); + + lldb::Format + GetFormat (); + + lldb::Encoding + GetEncoding (uint32_t &count); + + static lldb::Encoding + GetEncoding (void *clang_qual_type, uint32_t &count); + + SymbolContextScope * + GetSymbolContextScope() + { + return m_context; + } + const SymbolContextScope * + GetSymbolContextScope() const + { + return m_context; + } + void + SetSymbolContextScope(SymbolContextScope *context) + { + m_context = context; + } + + void * + GetOpaqueClangQualType (); + + clang::ASTContext * + GetClangAST (); + + ClangASTContext & + GetClangASTContext (); + + void * + GetChildClangTypeAtIndex (const char *parent_name, + uint32_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + ConstString& name, + uint32_t &child_byte_size, + int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset); + + static int + Compare(const Type &a, const Type &b); + + static lldb::Format + GetFormat (void *clang_qual_type); + + static int + DumpClangTypeName(Stream *s, void *clang_qual_type); + +protected: + ConstString m_name; + uint64_t m_byte_size; + SymbolFile *m_symbol_file; + SymbolContextScope *m_context; // The symbol context in which this type is defined + lldb::user_id_t m_encoding_uid; + EncodingUIDType m_encoding_uid_type; + Declaration m_decl; + void *m_clang_qual_type; + + bool ResolveClangType(); +}; + +} // namespace lldb_private + +#endif // liblldb_Type_h_ + diff --git a/lldb/include/lldb/Symbol/TypeList.h b/lldb/include/lldb/Symbol/TypeList.h new file mode 100644 index 000000000000..9a488dbc8bfe --- /dev/null +++ b/lldb/include/lldb/Symbol/TypeList.h @@ -0,0 +1,94 @@ +//===-- 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/ClangASTContext.h" +#include "lldb/Symbol/Type.h" +#include + +namespace lldb_private { + +class TypeList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TypeList(const char *target_triple = NULL); + + virtual + ~TypeList(); + + void + Clear(); + + void + Dump(Stream *s, bool show_context); + + lldb::TypeSP + FindType(lldb::user_id_t uid); + + TypeList + FindTypes(const ConstString &name); + + lldb::TypeSP + InsertUnique(lldb::TypeSP& type); + + uint32_t + GetSize() const; + + lldb::TypeSP + GetTypeAtIndex(uint32_t idx); + + //------------------------------------------------------------------ + // Classes that inherit from TypeList can see and modify these + //------------------------------------------------------------------ + ClangASTContext & + GetClangASTContext (); + + void * + CreateClangPointerType (Type *type); + + void * + CreateClangTypedefType (Type *typedef_type, Type *base_type); + + // For C++98 references (&) + void * + CreateClangLValueReferenceType (Type *type); + + // For C++0x references (&&) + void * + CreateClangRValueReferenceType (Type *type); + + void * + GetConstClangType (Type *type); + + void * + GetRestrictClangType (Type *type); + + void * + GetVolatileClangType (Type *type); + +private: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + ClangASTContext m_ast; ///< The type abtract syntax tree. + + collection m_types; + + DISALLOW_COPY_AND_ASSIGN (TypeList); +}; + +} // namespace lldb_private + +#endif // liblldb_TypeList_h_ diff --git a/lldb/include/lldb/Symbol/Variable.h b/lldb/include/lldb/Symbol/Variable.h new file mode 100644 index 000000000000..0844943e9caa --- /dev/null +++ b/lldb/include/lldb/Symbol/Variable.h @@ -0,0 +1,123 @@ +//===-- 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/Expression/DWARFExpression.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { + +class Variable : public UserID +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Variable(lldb::user_id_t uid, + const ConstString& name, Type *type, + lldb::ValueType scope, + SymbolContextScope *context, + Declaration* decl, + const DWARFExpression& location, + bool external, + bool artificial); + + virtual + ~Variable(); + + void + Dump(Stream *s, bool show_context) const; + + const Declaration& + GetDeclaration() const + { + return m_declaration; + } + + const ConstString& + GetName() const + { + return m_name; + } + + Type * + GetType() + { + return m_type; + } + + const Type * + GetType() const + { + return m_type; + } + + 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; + } + + size_t + MemorySize() const; + + void + CalculateSymbolContext (SymbolContext *sc); + + bool + IsInScope (StackFrame *frame); + +protected: + ConstString m_name; // Name of the variable + Type * m_type; // The type pointer of the variable (int, struct, class, etc) + lldb::ValueType m_scope; // global, parameter, local + SymbolContextScope *m_context;// 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 +private: + Variable(const Variable& rhs); + Variable& operator=(const Variable& rhs); +}; + +} // namespace lldb_private + +#endif // liblldb_Variable_h_ diff --git a/lldb/include/lldb/Symbol/VariableList.h b/lldb/include/lldb/Symbol/VariableList.h new file mode 100644 index 000000000000..53d21e659f8b --- /dev/null +++ b/lldb/include/lldb/Symbol/VariableList.h @@ -0,0 +1,74 @@ +//===-- 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 (lldb::VariableSP &var_sp); + + void + AddVariables(VariableList *variable_list); + + void + Clear(); + + void + Dump(Stream *s, bool show_context) const; + + lldb::VariableSP + GetVariableAtIndex(uint32_t idx); + + lldb::VariableSP + FindVariable(const ConstString& name); + +// const SymbolContext& +// GetSymbolContext() const +// { +// return m_symbol_context; +// } +// + 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/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h new file mode 100644 index 000000000000..54b5dc52211e --- /dev/null +++ b/lldb/include/lldb/Target/ABI.h @@ -0,0 +1,67 @@ +//===-- 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/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 arg) const = 0; + + virtual bool + PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const = 0; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const = 0; + + virtual bool + GetReturnValue (Thread &thread, + Value &value) const = 0; + + static ABI* + FindPlugin (const ConstString &triple); +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/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h new file mode 100644 index 000000000000..9dc0cb6d9bbb --- /dev/null +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -0,0 +1,154 @@ +//===-- 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/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; + + //------------------------------------------------------------------ + /// Get wether 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 wether 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 wether 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; + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Process* m_process; ///< The process that this dynamic loader plug-in is tracking. + bool m_stop_when_images_change; ///< Boolean value that indicates if the process should stop when imamges change. +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoader); + +}; + +} // namespace lldb_private + +#endif // liblldb_DynamicLoader_h_ diff --git a/lldb/include/lldb/Target/ExecutionContext.h b/lldb/include/lldb/Target/ExecutionContext.h new file mode 100644 index 000000000000..cc925cff9970 --- /dev/null +++ b/lldb/include/lldb/Target/ExecutionContext.h @@ -0,0 +1,93 @@ +//===-- ExecutionContext.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_ExecutionContext_h_ +#define liblldb_ExecutionContext_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @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 process, thread and frame. +/// +/// Many lldb functions can evaluate or act upon a specific +/// execution context. An expression could be evaluated for a specific +/// process, thread, and frame. The thread object contains frames and +/// can return StackFrame objects given a valid frame index using: +/// StackFrame * Thread::GetFrameAtIndex (uint32_t idx). +//---------------------------------------------------------------------- +class ExecutionContext +{ +public: + //------------------------------------------------------------------ + /// Default Constructor. + /// + /// Initialize with NULL process and thread, and invalid frame + /// index. + //------------------------------------------------------------------ + ExecutionContext(); + + + ExecutionContext (Target* t, bool fill_current_process_thread_frame = true); + //------------------------------------------------------------------ + /// 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 (ExecutionContextScope *exe_scope); + + ExecutionContext (ExecutionContextScope &exe_scope); + + //------------------------------------------------------------------ + /// 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; + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Target *target; ///< The target that owns the process/thread/frame + Process *process; ///< The process that owns the thread/frame + Thread *thread; ///< The thread that owns the frame + StackFrame *frame; ///< The stack frame in thread. +}; + +} // namespace lldb_private + +#endif // liblldb_ExecutionContext_h_ diff --git a/lldb/include/lldb/Target/ExecutionContextScope.h b/lldb/include/lldb/Target/ExecutionContextScope.h new file mode 100644 index 000000000000..f98e3276543e --- /dev/null +++ b/lldb/include/lldb/Target/ExecutionContextScope.h @@ -0,0 +1,71 @@ +//===-- 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 Target * + CalculateTarget () = 0; + + virtual Process * + CalculateProcess () = 0; + + virtual Thread * + CalculateThread () = 0; + + virtual StackFrame * + 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 + Calculate (ExecutionContext &exe_ctx) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_ExecutionContextScope_h_ diff --git a/lldb/include/lldb/Target/ObjCObjectPrinter.h b/lldb/include/lldb/Target/ObjCObjectPrinter.h new file mode 100644 index 000000000000..3b7005b09621 --- /dev/null +++ b/lldb/include/lldb/Target/ObjCObjectPrinter.h @@ -0,0 +1,49 @@ +//===-- ObjCObjectPrinter.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjCObjectPrinter_h_ +#define liblldb_ObjCObjectPrinter_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +// Project includes + +namespace lldb_private { + + class ObjCObjectPrinter + { + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ObjCObjectPrinter (Process &process); + + virtual + ~ObjCObjectPrinter (); + + bool + PrintObject (ConstString &str, Value &object_ptr, ExecutionContext &exe_ctx); + protected: + Process &m_process; + std::auto_ptr
m_PrintForDebugger_addr; + + Address *GetPrintForDebuggerAddr(); + private: + //------------------------------------------------------------------ + // For ObjCObjectPrinter only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ObjCObjectPrinter); + }; + +} // namespace lldb_private + +#endif // liblldb_ObjCObjectPrinter_h_ diff --git a/lldb/include/lldb/Target/PathMappingList.h b/lldb/include/lldb/Target/PathMappingList.h new file mode 100644 index 000000000000..3881437ffd43 --- /dev/null +++ b/lldb/include/lldb/Target/PathMappingList.h @@ -0,0 +1,82 @@ +//===-- 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 (ChangedCallback callback, + void *callback_baton); + + virtual + ~PathMappingList (); + + void + Append (const ConstString &path, const ConstString &replacement, bool notify); + + void + Clear (bool notify); + + void + Dump (Stream *s); + + size_t + GetSize (); + + void + Insert (const ConstString &path, + const ConstString &replacement, + uint32_t insert_idx, + bool notify); + + bool + Remove (off_t index, bool notify); + + bool + RemapPath (const ConstString &path, ConstString &new_path); + +protected: + typedef std::pair pair; + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_pairs; + ChangedCallback m_callback; + void * m_callback_baton; +private: + //------------------------------------------------------------------ + // For PathMappingList only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (PathMappingList); +}; + +} // namespace lldb_private + +#endif // liblldb_PathMappingList_h_ diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h new file mode 100644 index 000000000000..30663bd1c52e --- /dev/null +++ b/lldb/include/lldb/Target/Process.h @@ -0,0 +1,1415 @@ +//===-- 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 +// 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/Core/Event.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Core/ThreadSafeSTLMap.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Breakpoint/BreakpointSiteList.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/ObjCObjectPrinter.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/UnixSignals.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Process Process.h "lldb/Target/Process.h" +/// @brief A plug-in interface definition class for debugging a process. +//---------------------------------------------------------------------- +class Process : + public UserID, + public Broadcaster, + public ExecutionContextScope, + public PluginInterface +{ +friend class ThreadList; + +public: + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStateChanged = (1 << 0), + eBroadcastBitInterrupt = (1 << 1), + eBroadcastBitSTDOUT = (1 << 2), + eBroadcastBitSTDERR = (1 << 3), + }; + + enum + { + eBroadcastInternalStateControlStop = (1<<0), + eBroadcastInternalStateControlPause = (1<<1), + eBroadcastInternalStateControlResume = (1<<2) + }; + + //------------------------------------------------------------------ + /// 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 + { + 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; + + lldb::StateType + GetState() const; + + bool + GetRestarted () const; + + 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 void + SetRestartedInEvent (Event *event_ptr, bool new_value); + + static bool + SetUpdateStateOnRemoval (Event *event_ptr); + + + private: + + void + SetUpdateStateOnRemoval(); + + void + SetRestarted (bool new_value); + + lldb::ProcessSP m_process_sp; + lldb::StateType m_state; + bool m_restarted; // For "eStateStopped" events, this is true if the target was automatically restarted. + bool m_update_state; + DISALLOW_COPY_AND_ASSIGN (ProcessEventData); + + }; +#endif + + //------------------------------------------------------------------ + /// 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 Process* + FindPlugin (Target &target, const char *plugin_name, Listener &listener); + + + + //------------------------------------------------------------------ + /// 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 + int signo, // Zero for no signal + int status); // Exit value of process if signal is zero + + //------------------------------------------------------------------ + /// Check if a plug-in instance can debug the file in \a module. + /// + /// Each plug-in is given a chance to say wether 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) = 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(); + + //------------------------------------------------------------------ + /// 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] 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. + /// + /// @return + /// An error object. Call GetID() to get the process ID if + /// the error object is success. + //------------------------------------------------------------------ + virtual Error + Launch (char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path); + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call Process::WillAttach (lldb::pid_t) + /// and if that returns \b true, Process::DoAttach (lldb::pid_t) 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 (lldb::pid_t pid); + + //------------------------------------------------------------------ + /// 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 Error + Attach (const char *process_name, bool wait_for_launch); + + uint32_t + GetAddressByteSize(); + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// 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. + /// + /// @return + /// Returns an error object. + /// + /// @see Thread:Resume() + /// @see Thread:Step() + /// @see Thread:Suspend() + //------------------------------------------------------------------ + virtual Error + Resume (); + + //------------------------------------------------------------------ + /// Halts a running process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + Halt (); + + //------------------------------------------------------------------ + /// Detaches from a running or stopped process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + Detach (); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + virtual 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. + //------------------------------------------------------------------ + virtual Error + Signal (int signal); + + virtual UnixSignals & + GetUnixSignals (); + + + //================================================================== + // 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 + WillAttach (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 + WillAttach (const char *process_name, bool wait_for_launch) + { + 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 + DoAttach (lldb::pid_t pid) = 0; + + //------------------------------------------------------------------ + /// 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. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + DoAttach (const char *process_name, bool wait_for_launch) + { + 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 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] 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. + /// + /// @return + /// A new valid process ID, or LLDB_INVALID_PROCESS_ID if + /// launching fails. + //------------------------------------------------------------------ + virtual Error + DoLaunch (Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path) = 0; + + //------------------------------------------------------------------ + /// 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 () = 0; + + //------------------------------------------------------------------ + /// 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. + /// + /// @return + /// Returns \b true if the process successfully halts, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual Error + DoHalt () = 0; + + //------------------------------------------------------------------ + /// 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 () = 0; + + //------------------------------------------------------------------ + /// Called after detaching from a process. + /// + /// Allow Process plug-ins to execute some code after detaching + /// from a process. + //------------------------------------------------------------------ + virtual void + DidDetach () {} + + //------------------------------------------------------------------ + /// 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) = 0; + + + + virtual Error + WillDestroy () { return Error(); } + + virtual Error + DoDestroy () = 0; + + virtual void + DidDestroy () { } + + + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// Get accessor for the current process state. + /// + /// @return + /// The current state of the process. + /// + /// @see lldb::StateType + //------------------------------------------------------------------ + lldb::StateType + GetState (); + +protected: + friend class CommandObjectProcessLaunch; + friend class ProcessEventData; + friend class CommandObjectBreakpointCommand; + + void + SetState (lldb::EventSP &event_sp); + + lldb::StateType + GetPrivateState (); + +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 number of times this process has posted a stop event. + /// + /// @return + /// The number of times this process has stopped while being + /// debugged. + //------------------------------------------------------------------ + uint32_t + GetStopID () const; + + //------------------------------------------------------------------ + /// 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 void + 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; + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + size_t + ReadMemory (lldb::addr_t vm_addr, + void *buf, + size_t size, + 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. + /// + /// @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) = 0; + + //------------------------------------------------------------------ + /// 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) = 0; + + //------------------------------------------------------------------ + /// 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. + /// + /// @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); + + //------------------------------------------------------------------ + /// 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) = 0; + + //------------------------------------------------------------------ + /// 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) + { + error.SetErrorString("stdout unsupported"); + return 0; + } + + + //------------------------------------------------------------------ + /// 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) + { + error.SetErrorString("stderr unsupported"); + return 0; + } + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, Error &error) + { + error.SetErrorString("stdin unsupported"); + return 0; + } + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site) = 0; + + virtual Error + EnableBreakpoint (BreakpointSite *bp_site) = 0; + + virtual Error + DisableBreakpoint (BreakpointSite *bp_site) = 0; + + // 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::user_id_t + CreateBreakpointSite (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. This has to + // be a static function because you can't be sure that removing the + // breakpoint from it's containing map won't delete the breakpoint site, + // and doing that in an instance method isn't copasetic. + 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 (WatchpointLocation *bp_loc); + + virtual Error + DisableWatchpoint (WatchpointLocation *bp_loc); + + //------------------------------------------------------------------ + // Thread Queries + //------------------------------------------------------------------ + virtual uint32_t + UpdateThreadListIfNeeded () = 0; + + ThreadList & + GetThreadList (); + + const ThreadList & + GetThreadList () const; + + uint32_t + GetNextThreadIndexID (); + + //------------------------------------------------------------------ + // Event Handling + //------------------------------------------------------------------ + lldb::StateType + GetNextEvent (lldb::EventSP &event_sp); + + lldb::StateType + WaitForProcessToStop (const TimeValue *timeout); + + lldb::StateType + WaitForStateChangedEvents (const TimeValue *timeout, lldb::EventSP &event_sp); + + Event * + PeekAtStateChangedEvents (); + + //------------------------------------------------------------------ + /// 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. + /// + /// @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); + + //------------------------------------------------------------------ + /// Gets the byte order for this process. + /// + /// @return + /// A valid ByteOrder enumeration, or eByteOrderInvalid. + //------------------------------------------------------------------ + virtual lldb::ByteOrder + GetByteOrder () const = 0; + + const ConstString & + GetTargetTriple () + { + return m_target_triple; + } + + const ABI * + GetABI (); + + virtual DynamicLoader * + GetDynamicLoader (); + + lldb::addr_t + GetSectionLoadAddress (const Section *section) const; + + bool + ResolveLoadAddress (lldb::addr_t load_addr, Address &so_addr) const; + + bool + SectionLoaded (const Section *section, lldb::addr_t load_addr); + + // 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 + SectionUnloaded (const Section *section, 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 + SectionUnloaded (const Section *section); + + bool + IsRunning () const; + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual Target * + CalculateTarget (); + + virtual Process * + CalculateProcess (); + + virtual Thread * + CalculateThread (); + + virtual StackFrame * + CalculateStackFrame (); + + virtual void + Calculate (ExecutionContext &exe_ctx); + + lldb::ProcessSP + GetSP (); + + ObjCObjectPrinter & + GetObjCObjectPrinter(); + +protected: + typedef ThreadSafeSTLMap SectionLoadColl; + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Target & m_target; ///< The target that owns this process. + SectionLoadColl m_section_load_info; ///< A mapping of all currently loaded sections. + 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 + uint32_t m_stop_id; ///< A count of many times the process has stopped. + uint32_t m_thread_index_id; ///< Each thread is created with a 1 based index that won't get re-used. + 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. + ThreadList m_thread_list; ///< The threads for this process. + std::vector m_notifications; ///< The list of notifications that this process can deliver. + Listener &m_listener; + BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint locations we intend + ///< to insert in the target. + UnixSignals m_unix_signals; /// This is the current signal set for this process. + ConstString m_target_triple; + lldb::ABISP m_abi_sp; + ObjCObjectPrinter m_objc_object_printer; + + 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); + + void + SetPrivateState (lldb::StateType state); + + bool + StartPrivateStateThread (); + + 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); + + Error + CompleteAttach (); + + + // 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); + +private: + //------------------------------------------------------------------ + // For Process only + //------------------------------------------------------------------ + void ControlPrivateStateThread (uint32_t signal); + DISALLOW_COPY_AND_ASSIGN (Process); + +}; + +} // namespace lldb_private + +#endif // liblldb_Process_h_ diff --git a/lldb/include/lldb/Target/RegisterContext.h b/lldb/include/lldb/Target/RegisterContext.h new file mode 100644 index 000000000000..5c646cf600ba --- /dev/null +++ b/lldb/include/lldb/Target/RegisterContext.h @@ -0,0 +1,172 @@ +//===-- 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 ExecutionContextScope +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContext (Thread &thread, StackFrame *frame); + + virtual + ~RegisterContext (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + Invalidate () = 0; + + virtual size_t + GetRegisterCount () = 0; + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg) = 0; + + virtual size_t + GetRegisterSetCount () = 0; + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t reg_set) = 0; + + virtual bool + ReadRegisterValue (uint32_t reg, Scalar &value) = 0; + + virtual bool + ReadRegisterBytes (uint32_t reg, DataExtractor &data) = 0; + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) = 0; + + virtual bool + WriteRegisterValue (uint32_t reg, const Scalar &value) = 0; + + virtual bool + WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset = 0) = 0; + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) = 0; + + 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); + + //------------------------------------------------------------------ + // Subclasses should not override these + //------------------------------------------------------------------ + lldb::tid_t + GetThreadID() const; + + const lldb::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); + + bool + WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual Target * + CalculateTarget (); + + virtual Process * + CalculateProcess (); + + virtual Thread * + CalculateThread (); + + virtual StackFrame * + CalculateStackFrame (); + + virtual void + Calculate (ExecutionContext &exe_ctx); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from RegisterContext can see and modify these + //------------------------------------------------------------------ + Thread &m_thread; // The thread that this register context belongs to. + StackFrame *m_frame; // The stack frame for this context, or NULL if this is the root context +private: + //------------------------------------------------------------------ + // For RegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContext); +}; + +} // namespace lldb_private + +#endif // liblldb_RegisterContext_h_ diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h new file mode 100644 index 000000000000..0c46ec278eb6 --- /dev/null +++ b/lldb/include/lldb/Target/StackFrame.h @@ -0,0 +1,126 @@ +//===-- 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 UserID, + public ExecutionContextScope +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackFrame (lldb::user_id_t frame_idx, Thread &thread, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr = NULL); + StackFrame (lldb::user_id_t frame_idx, Thread &thread, lldb::RegisterContextSP ®_context_sp, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr = NULL); + virtual ~StackFrame (); + + Thread & + GetThread() + { return m_thread; } + + const Thread & + GetThread() const + { return m_thread; } + + StackID& + GetStackID(); + + Address& + GetPC(); + + void + ChangePC (lldb::addr_t pc); + + const SymbolContext& + GetSymbolContext (uint32_t resolve_scope); + + bool + GetFrameBaseValue(Scalar &value, Error *error_ptr); + + RegisterContext * + GetRegisterContext (); + + VariableList * + GetVariableList (); + + bool + HasDebugInformation (); + + ValueObjectList & + GetValueObjectList(); + + const char * + Disassemble (); + + void + Dump (Stream *strm, bool show_frame_index); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual Target * + CalculateTarget (); + + virtual Process * + CalculateProcess (); + + virtual Thread * + CalculateThread (); + + virtual StackFrame * + CalculateStackFrame (); + + virtual void + Calculate (ExecutionContext &exe_ctx); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StackFrame can see and modify these + //------------------------------------------------------------------ + + +private: + //------------------------------------------------------------------ + // For StackFrame only + //------------------------------------------------------------------ + Thread &m_thread; + lldb::RegisterContextSP m_reg_context_sp; + StackID m_id; + Address m_pc; // PC 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_value_object_list; + StreamString m_disassembly; + DISALLOW_COPY_AND_ASSIGN (StackFrame); +}; + +} // namespace lldb_private + +#endif // liblldb_StackFrame_h_ diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h new file mode 100644 index 000000000000..678bff5a3d86 --- /dev/null +++ b/lldb/include/lldb/Target/StackFrameList.h @@ -0,0 +1,88 @@ +//===-- 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: + friend class Thread; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackFrameList(); + + virtual + ~StackFrameList(); + + uint32_t + GetNumFrames() const; + + lldb::StackFrameSP + GetFrameAtIndex (uint32_t idx) const; + + bool + SetFrameAtIndex (uint32_t idx, lldb::StackFrameSP &frame_sp); + + // Mark a stack frame as the current frame + uint32_t + SetCurrentFrame (lldb_private::StackFrame *frame); + + uint32_t + GetCurrentFrameIndex () const; + + // Mark a stack frame as the current frame using the frame index + void + SetCurrentFrameByIndex (uint32_t idx); + + void + Clear (); + + // After we have determined the number of frames, we can set the count here + // and have the frame info be generated on demand. + void + SetNumFrames(uint32_t count); + + void + InvalidateFrames (uint32_t start_idx); +protected: + + //------------------------------------------------------------------ + // Classes that inherit from StackFrameList can see and modify these + //------------------------------------------------------------------ + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + mutable Mutex m_mutex; + collection m_frames; + uint32_t m_current_frame_idx; + +private: + //------------------------------------------------------------------ + // For StackFrameList only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (StackFrameList); +}; + +} // namespace lldb_private + +#endif // liblldb_StackFrameList_h_ diff --git a/lldb/include/lldb/Target/StackID.h b/lldb/include/lldb/Target/StackID.h new file mode 100644 index 000000000000..cf02be1c925a --- /dev/null +++ b/lldb/include/lldb/Target/StackID.h @@ -0,0 +1,65 @@ +//===-- 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 (); + explicit StackID (lldb::addr_t cfa); + StackID (const Address& start_address, lldb::addr_t cfa); + StackID (const StackID& rhs); + virtual ~StackID(); + + const Address& + GetStartAddress() const; + + void + SetStartAddress(const Address& start_address); + + lldb::addr_t + GetCallFrameAddress() const; + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const StackID& + operator=(const StackID& rhs); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StackID can see and modify these + //------------------------------------------------------------------ + Address m_start_address; // The address range for the function for this frame + lldb::addr_t m_cfa; // The call frame address (stack pointer) value + // at the beginning of the function that uniquely + // identifies this frame +}; + +bool operator== (const StackID& lhs, const StackID& rhs); +bool operator!= (const StackID& lhs, const StackID& rhs); +bool operator< (const StackID& lhs, const StackID& rhs); + +} // namespace lldb_private + +#endif // liblldb_StackID_h_ diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h new file mode 100644 index 000000000000..0f9fd09b9850 --- /dev/null +++ b/lldb/include/lldb/Target/Target.h @@ -0,0 +1,324 @@ +//===-- 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 + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/BreakpointList.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/PathMappingList.h" + +#include "lldb/API/SBTarget.h" + +namespace lldb_private { + +class Target : + public Broadcaster, + public ExecutionContextScope +{ +public: + friend class TargetList; + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2) + }; + + lldb::ModuleSP + GetSharedModule (const FileSpec& file_spec, + const ArchSpec& arch, + const UUID *uuid = NULL, + const ConstString *object_name = NULL, + off_t object_offset = 0, + Error *error_ptr = NULL); +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(); + +public: + ~Target(); + + void + DeleteCurrentProcess (); + + //------------------------------------------------------------------ + /// 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); + + const lldb::ProcessSP & + CreateProcess (Listener &listener, const char *plugin_name = NULL); + + const lldb::ProcessSP & + GetProcessSP () const; + + lldb::TargetSP + GetSP(); + + + //------------------------------------------------------------------ + // This part handles the breakpoints. + //------------------------------------------------------------------ + + BreakpointList & + GetBreakpointList(bool internal = false); + + const BreakpointList & + GetBreakpointList(bool internal = false) const; + + 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 FileSpec *containingModule, + const FileSpec &file, + uint32_t line_no, + bool check_inlines, + 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, or all modules if it is NULL + lldb::BreakpointSP + CreateBreakpoint (FileSpec *containingModule, + RegularExpression &func_regexp, + bool internal = false); + + // Use this to create a function breakpoint by name in containingModule, or all modules if it is NULL + lldb::BreakpointSP + CreateBreakpoint (FileSpec *containingModule, + const char *func_name, + bool internal = false); + + // Use this to create a general breakpoint: + lldb::BreakpointSP + CreateBreakpoint (lldb::SearchFilterSP &filter_sp, + lldb::BreakpointResolverSP &resolver_sp, + bool internal = false); + + 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); + + void + ModulesDidLoad (ModuleList &module_list); + + void + ModulesDidUnload (ModuleList &module_list); + +protected: + void + ModuleAdded (lldb::ModuleSP &module_sp); + + void + ModuleUpdated (lldb::ModuleSP &old_module_sp, lldb::ModuleSP &new_module_sp); + +public: + //------------------------------------------------------------------ + /// 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 (); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + ModuleList& + GetImages (); + + ArchSpec + GetArchitecture () const; + + bool + GetTargetTriple (ConstString &target_triple); + + size_t + ReadMemory (lldb::AddressType addr_type, + lldb::addr_t addr, + void *buf, + size_t size, + Error &error, + ObjectFile* objfile = NULL); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual Target * + CalculateTarget (); + + virtual Process * + CalculateProcess (); + + virtual Thread * + CalculateThread (); + + virtual StackFrame * + CalculateStackFrame (); + + virtual void + Calculate (ExecutionContext &exe_ctx); + + PathMappingList & + GetImageSearchPathList (); + + ClangASTContext * + GetScratchClangASTContext(); + +protected: + friend class lldb::SBTarget; + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + ModuleList m_images; ///< The list of images for this process (shared libraries and anything dynamically loaded). + BreakpointList m_breakpoint_list; + BreakpointList m_internal_breakpoint_list; + // 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; + ConstString m_triple; ///< The target triple ("x86_64-apple-darwin10") + lldb::SearchFilterSP m_search_filter_sp; + PathMappingList m_image_search_paths; + std::auto_ptr m_scratch_ast_context_ap; + //------------------------------------------------------------------ + // Methods. + //------------------------------------------------------------------ + lldb::SearchFilterSP + GetSearchFilterForModule (const FileSpec *containingModule); + + 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/lldb/include/lldb/Target/TargetList.h b/lldb/include/lldb/Target/TargetList.h new file mode 100644 index 000000000000..8cd7b7b971db --- /dev/null +++ b/lldb/include/lldb/Target/TargetList.h @@ -0,0 +1,206 @@ +//===-- 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(); + +public: + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitInterrupt = (1 << 0) + }; + + + ~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] 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] arch + /// The architecture to use when launching the \a file_spec for + /// debugging. This can be NULL if the architecture is not known + /// or when attaching to a process. + /// + /// @param[in] uuid_ptr + /// An optional UUID to use when loading a target. When this is + /// specified, plug-ins might be able to track down a different + /// executable than the one on disk specified by "file_spec" in + /// an alternate SDK or build location (such as when doing + /// symbolication on non-native OS builds). + /// + /// @return + /// A shared pointer to a target object. + //------------------------------------------------------------------ + Error + CreateTarget (const FileSpec& file_spec, + const ArchSpec& arch, + const UUID *uuid_ptr, + bool get_dependent_files, + 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; + + //------------------------------------------------------------------ + /// 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 + SetCurrentTarget (Target *target); + + void + SetCurrentTargetWithIndex (uint32_t idx); + + lldb::TargetSP + GetCurrentTarget (); + + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_target_list; + mutable Mutex m_target_list_mutex; + uint32_t m_current_target_idx; +private: + DISALLOW_COPY_AND_ASSIGN (TargetList); +}; + +} // namespace lldb_private + +#endif // liblldb_TargetList_h_ diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h new file mode 100644 index 000000000000..cc5dcf89db0f --- /dev/null +++ b/lldb/include/lldb/Target/Thread.h @@ -0,0 +1,701 @@ +//===-- 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/UserID.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrameList.h" + +#define LLDB_THREAD_MAX_STOP_EXC_DATA 8 + +// I forward declare these here so I don't have to #include ThreadPlan, so in turn I +// can use Thread.h in ThreadPlan.h. + +namespace lldb_private { + +class Thread : + public UserID, + public ExecutionContextScope +{ +friend class ThreadPlan; +public: + //---------------------------------------------------------------------- + // StopInfo + // + // Describes the reason the thread it was created with stopped. + //---------------------------------------------------------------------- + class StopInfo + { + public: + StopInfo(Thread *thread = NULL); + + ~StopInfo(); + + // Clear clears the stop reason, but it does not clear the thread this + // StopInfo is tied to. + void + Clear(); + + lldb::StopReason + GetStopReason() const; + + void + SetThread (Thread *thread); + + Thread * + GetThread (); + + void + SetStopReasonWithBreakpointSiteID (lldb::user_id_t break_id); + + void + SetStopReasonWithWatchpointID (lldb::user_id_t watch_id); + + void + SetStopReasonWithSignal (int signo); + + void + SetStopReasonToTrace (); + + void + SetStopReasonWithException (uint32_t exc_type, size_t exc_data_count); + + void + SetStopReasonWithPlan (lldb::ThreadPlanSP &plan); + + void + SetStopReasonToNone (); + + const char * + GetStopDescription() const; + + void + SetStopDescription(const char *desc); + + lldb::user_id_t + GetBreakpointSiteID() const; + + lldb::user_id_t + GetWatchpointID() const; + + int + GetSignal() const; + + lldb::user_id_t + GetPlanID () const; + + uint32_t + GetExceptionType() const; + + size_t + GetExceptionDataCount() const; + + lldb::addr_t + GetExceptionDataAtIndex (uint32_t idx) const; + + bool + SetExceptionDataAtIndex (uint32_t idx, lldb::addr_t data); + + void + Dump (Stream *s) const; + + protected: + lldb::StopReason m_reason; + //-------------------------------------------------------------- + // For eStopReasonPlan the completed plan is stored in this shared pointer. + //-------------------------------------------------------------- + lldb::ThreadPlanSP m_completed_plan_sp; + Thread *m_thread; + char m_description[256]; + union + { + //-------------------------------------------------------------- + // eStopReasonBreakpoint + //-------------------------------------------------------------- + struct + { + lldb::user_id_t bp_site_id; + } breakpoint; + //-------------------------------------------------------------- + // eStopReasonWatchpoint + //-------------------------------------------------------------- + struct + { + lldb::user_id_t watch_id; + } watchpoint; + //-------------------------------------------------------------- + // eStopReasonSignal + //-------------------------------------------------------------- + struct + { + int signo; + } signal; + //-------------------------------------------------------------- + // eStopReasonException + //-------------------------------------------------------------- + struct + { + uint32_t type; + size_t data_count; + lldb::addr_t data[LLDB_THREAD_MAX_STOP_EXC_DATA]; + } exception; + } m_details; + }; + + 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 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; + }; + + Thread (Process &process, lldb::tid_t tid); + virtual ~Thread(); + + Process & + GetProcess() { return m_process; } + + const Process & + GetProcess() const { return m_process; } + + int + GetResumeSignal () const; + + void + SetResumeSignal (int signal); + + lldb::StateType + GetState() const; + + lldb::ThreadSP + GetSP (); + + void + SetState (lldb::StateType state); + + lldb::StateType + GetResumeState () const; + + void + SetResumeState (lldb::StateType state); + + // This function is called on all the threads before "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 (); + + // Override this to do platform specific tasks before resume, but always + // call the Thread::WillResume at the end of your work. + + virtual bool + 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 (); + + virtual void + RefreshStateAfterStop() = 0; + + void + WillStop (); + + bool + ShouldStop (Event *event_ptr); + + lldb::Vote + ShouldReportStop (Event *event_ptr); + + lldb::Vote + ShouldReportRun (Event *event_ptr); + + bool + GetStopInfo (StopInfo *stop_info); + + bool + ThreadStoppedForAReason (); + + virtual const char * + GetInfo () = 0; + + virtual const char * + GetName () + { + return NULL; + } + + virtual const char * + GetQueueName () + { + return NULL; + } + + virtual uint32_t + GetStackFrameCount() = 0; + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx) = 0; + + lldb::StackFrameSP + GetCurrentFrame (); + + uint32_t + SetCurrentFrame (lldb_private::StackFrame *frame); + + void + SetCurrentFrameByIndex (uint32_t frame_idx); + + virtual RegisterContext * + GetRegisterContext () = 0; + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint) = 0; + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint) = 0; + + virtual RegisterContext * + CreateRegisterContextForFrame (StackFrame *frame) = 0; + + virtual void + ClearStackFrames () + { + m_frames.Clear(); + } + + void + DumpInfo (Stream &strm, + bool show_stop_reason, + bool show_name, + bool show_queue, + uint32_t frame_idx);// = UINT32_MAX); + + //------------------------------------------------------------------ + // 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. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + QueueThreadPlanForStepSingleInstruction (bool step_over, + bool abort_other_plans, + bool stop_other_threads); + + //------------------------------------------------------------------ + /// Queues the plan used to step through an address range, stepping into or over + /// function calls depending on the value of StepType. + /// + /// @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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + QueueThreadPlanForStepRange (bool abort_other_plans, + lldb::StepType type, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_other_threads); + + //------------------------------------------------------------------ + /// 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + QueueThreadPlanForStepOut (bool abort_other_plans, + SymbolContext *addr_context, + bool first_insn, + bool stop_other_threads, + lldb::Vote stop_vote = lldb::eVoteYes, + lldb::Vote run_vote = lldb::eVoteNoOpinion); + + //------------------------------------------------------------------ + /// 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] 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + QueueThreadPlanForStepThrough (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] 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + QueueThreadPlanForContinue (bool abort_other_plans, + bool stop_other_threads, + lldb::Vote stop_vote, + lldb::Vote run_vote = lldb::eVoteNoOpinion, + bool immediate = false); + //------------------------------------------------------------------ + /// 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 pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual ThreadPlan * + QueueThreadPlanForRunToAddress (bool abort_other_plans, + Address &target_addr, + bool stop_other_threads); + + virtual ThreadPlan * + QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others); + + virtual ThreadPlan * + QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + lldb::addr_t arg, + bool stop_other_threads, + bool discard_on_error = false); + + virtual ThreadPlan * + QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + ValueList &args, + bool stop_other_threads, + bool discard_on_error = false); + + //------------------------------------------------------------------ + // Thread Plan accessors: + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Gets the plan which will execute next on the plan stack. + /// + /// @return + /// A pointer to the next executed plan. + //------------------------------------------------------------------ + ThreadPlan * + GetCurrentPlan (); + + //------------------------------------------------------------------ + /// Gets the inner-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 (); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Prints the current plan stack. + /// + /// @param[in] s + /// The stream to which to dump the plan stack info. + /// + //------------------------------------------------------------------ + void + DumpThreadPlans (Stream *s) const; + + // 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; + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual Target * + CalculateTarget (); + + virtual Process * + CalculateProcess (); + + virtual Thread * + CalculateThread (); + + virtual StackFrame * + CalculateStackFrame (); + + virtual void + Calculate (ExecutionContext &exe_ctx); + +protected: + void + PushPlan (lldb::ThreadPlanSP &plan_sp); + + void + PopPlan (); + + void + DiscardPlan (); + + ThreadPlan *GetPreviousPlan (ThreadPlan *plan); + + virtual bool + GetRawStopReason (StopInfo *stop_info) = 0; + + typedef std::vector plan_stack; + + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + Process & m_process; ///< The process that owns this thread. + 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. + plan_stack m_plan_stack; ///< The stack of plans this thread is executing. + plan_stack m_immediate_plan_stack; ///< The plans that need to get executed before any other work gets done. + 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_state_mutex; ///< Multithreaded protection for m_state. + StackFrameList m_frames; ///< The stack frames that get lazily populated after a thread stops. + uint32_t m_current_frame_idx;///< The current frame for this thread + int m_resume_signal; ///< The signal that should be used when continuing this thread. + lldb::StateType m_resume_state; ///< The state that indicates what this thread should do when the process is resumed. +private: + //------------------------------------------------------------------ + // For Thread only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Thread); + +}; + +} // namespace lldb_private + +#endif // liblldb_Thread_h_ diff --git a/lldb/include/lldb/Target/ThreadList.h b/lldb/include/lldb/Target/ThreadList.h new file mode 100644 index 000000000000..4f82cc7ca650 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadList.h @@ -0,0 +1,120 @@ +//===-- 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/Host/Mutex.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 (lldb::ThreadSP &thread_sp); + + lldb::ThreadSP + GetCurrentThread (); + + bool + SetCurrentThreadByID (lldb::tid_t tid); + + bool + SetCurrentThreadByIndexID (uint32_t index_id); + + void + Clear(); + + // 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 + FindThreadByIndexID (lldb::tid_t index_id, bool can_update = true); + + lldb::ThreadSP + GetThreadSPForThreadPtr (Thread *thread_ptr); + + bool + ShouldStop (Event *event_ptr); + + lldb::Vote + ShouldReportStop (Event *event_ptr); + + lldb::Vote + ShouldReportRun (Event *event_ptr); + + void + RefreshStateAfterStop (); + + bool + WillResume (); + + void + DidResume (); + + void + DiscardThreadPlans(); + + uint32_t + GetStopID () const; + + void + SetStopID (uint32_t stop_id); + +protected: + + 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. + mutable Mutex m_threads_mutex; + lldb::tid_t m_current_tid; ///< For targets that need the notion of a current thread. + +private: + ThreadList (); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadList_h_ diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h new file mode 100644 index 000000000000..96a132647376 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlan.h @@ -0,0 +1,358 @@ +//===-- 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" + +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. +// +// +// DEPRECATED: This ended up causing a real hassle, too many cases where the immediate plan +// got stranded. So the better way to do this is to post any plans you need to do right before +// running in the PrepareToResume method. +//f +// Immediate Plans: +// +// One other complexity of the plan stack is that sometimes you need to do a piece of work immediately +// on resume, regardless of what other plans have been pushed on the stack while the process has +// been stopped. The classic example is stepping over a breakpoint. To that end the plan stack is +// actually two stacks, an "immediate" plan stack and the normal plan stack. A plan can indicate that it +// should go on the immediate plan stack by returning "true" from the IsImmediate method. +// +// END DEPRECATED... +// +// 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. +// +// When the plan is moved from the plan stack to the completed plan stack its DidPop method is called. You should +// undo anything that affects target state in this method so the target state is clear for new plans. +// But be sure to leave whatever state might be needed to correctly fill the StopInfo. +// +// 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. +// 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. +// 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. +// In the normal case, this will just 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. +// +// 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. +// +// 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; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlan (const char *name, + Thread &thread, + lldb::Vote stop_vote, + lldb::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; + + //------------------------------------------------------------------ + /// Returns the Thread that is using this thread plan. + /// + /// @return + /// A pointer to the thread plan's owning thread. + //------------------------------------------------------------------ + Thread & + GetThread(); + + const Thread & + GetThread() const; + + //------------------------------------------------------------------ + /// 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 needs to be executed immediatly on resume. + /// + /// @return + /// \b true if the plan is immediate, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + IsImmediate() const + { + return false; + } + + //------------------------------------------------------------------ + /// 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. + /// + /// @return + /// \b true if the plan should be queued, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ValidatePlan (Stream *error) = 0; + + virtual bool + PlanExplainsStop () = 0; + + + virtual lldb::StateType + RunState () = 0; + + virtual bool + ShouldStop (Event *event_ptr) = 0; + + // 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 lldb::Vote + ShouldReportStop (Event *event_ptr); + + virtual lldb::Vote + ShouldReportRun (Event *event_ptr); + + virtual bool + StopOthers (); + + virtual bool + WillResume (lldb::StateType resume_state, bool current_plan); + + virtual bool + WillStop () = 0; + + virtual bool + IsMasterPlan() + { + return false; + } + + 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 (); + + bool + GetPrivate (); + + void + SetPrivate (bool 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); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlan can see and modify these + //------------------------------------------------------------------ + + bool + IsPlanComplete(); + + void + SetPlanComplete (); + + // 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 (); + + Thread &m_thread; + lldb::Vote m_stop_vote; + lldb::Vote m_run_vote; + +private: + //------------------------------------------------------------------ + // For ThreadPlan only + //------------------------------------------------------------------ + static lldb::user_id_t GetNextID (); + + std::string m_name; + Mutex m_plan_complete_mutex; + bool m_plan_complete; + bool m_plan_private; + bool m_okay_to_discard; + +private: + DISALLOW_COPY_AND_ASSIGN(ThreadPlan); +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlan_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanBase.h b/lldb/include/lldb/Target/ThreadPlanBase.h new file mode 100644 index 000000000000..6a0dbfc405a0 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanBase.h @@ -0,0 +1,66 @@ +//===-- 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/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 +{ +public: + virtual ~ThreadPlanBase (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + + virtual bool IsMasterPlan() + { + return true; + } + + virtual bool OkayToDiscard() + { + return false; + } + +protected: + ThreadPlanBase (Thread &thread); + +private: + friend ThreadPlan * + Thread::QueueFundamentalPlan(bool abort_other_plans); + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanBase); +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanFundamental_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h new file mode 100644 index 000000000000..53b2a87afa7f --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -0,0 +1,96 @@ +//===-- 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 +{ +public: + ThreadPlanCallFunction (Thread &thread, + Address &function, + lldb::addr_t arg, + bool stop_other_threads, + bool discard_on_error = true); + + ThreadPlanCallFunction (Thread &thread, + Address &function, + ValueList &args, + bool stop_other_threads, + bool discard_on_error = true); + + virtual + ~ThreadPlanCallFunction (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + PlanExplainsStop (); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual bool + StopOthers (); + + virtual void + SetStopOthers (bool new_value); + + virtual lldb::StateType + RunState (); + + virtual void + DidPush (); + + virtual bool + WillStop (); + + virtual bool + MischiefManaged (); + + virtual bool + IsMasterPlan() + { + return true; + } + +protected: +private: + bool m_use_abi; + bool m_valid; + bool m_stop_other_threads; + Address m_function_addr; + Address m_start_addr; + lldb::addr_t m_arg_addr; + ValueList *m_args; + Process &m_process; + Thread &m_thread; + Thread::RegisterCheckpoint m_register_backup; + lldb::ThreadPlanSP m_subplan_sp; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanCallFunction_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanContinue.h b/lldb/include/lldb/Target/ThreadPlanContinue.h new file mode 100644 index 000000000000..bd50c737fd7e --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanContinue.h @@ -0,0 +1,60 @@ +//===-- ThreadPlanContinue.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanContinue_h_ +#define liblldb_ThreadPlanContinue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanContinue : public ThreadPlan +{ +public: + ThreadPlanContinue (Thread &thread, + bool stop_others, + lldb::Vote stop_vote, + lldb::Vote run_vote, + bool immediate = false); + virtual ~ThreadPlanContinue (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool ValidatePlan (Stream *error); + + virtual bool PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool IsImmediate () const; + virtual bool WillResume (lldb::StateType resume_state, bool current_plan); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + bool InRange(); +private: + bool m_stop_others; + bool m_did_run; + bool m_immediate; + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanContinue); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanContinue_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanRunToAddress.h b/lldb/include/lldb/Target/ThreadPlanRunToAddress.h new file mode 100644 index 000000000000..3b591ea33244 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanRunToAddress.h @@ -0,0 +1,78 @@ +//===-- 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 +// 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); + + virtual + ~ThreadPlanRunToAddress (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + PlanExplainsStop (); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual bool + StopOthers (); + + virtual void + SetStopOthers (bool new_value); + + virtual lldb::StateType + RunState (); + + virtual bool + WillStop (); + + virtual bool + MischiefManaged (); + +protected: + void SetInitialBreakpoint(); + bool AtOurAddress(); +private: + bool m_stop_others; + lldb::addr_t m_address; // This is the address we are going to run to. + // TODO: Would it be useful to have multiple addresses? + lldb::user_id_t m_break_id; // 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/lldb/include/lldb/Target/ThreadPlanShouldStopHere.h b/lldb/include/lldb/Target/ThreadPlanShouldStopHere.h new file mode 100644 index 000000000000..66270dfa5a3b --- /dev/null +++ b/lldb/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); + + ThreadPlan * + 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/lldb/include/lldb/Target/ThreadPlanStepInRange.h b/lldb/include/lldb/Target/ThreadPlanStepInRange.h new file mode 100644 index 000000000000..2e0b4cb83858 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepInRange.h @@ -0,0 +1,76 @@ +//===-- 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: + virtual + ~ThreadPlanStepInRange (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ShouldStop (Event *event_ptr); + + static ThreadPlan * + DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton); + + static void + SetDefaultFlagValue (uint32_t new_value); + +protected: + + ThreadPlanStepInRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + virtual void + SetFlagsToDefault (); + +private: + + friend ThreadPlan * + Thread::QueueThreadPlanForStepRange (bool abort_other_plans, + lldb::StepType type, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + static uint32_t s_default_flag_values; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepInRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepInRange_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepInstruction.h b/lldb/include/lldb/Target/ThreadPlanStepInstruction.h new file mode 100644 index 000000000000..2157e84d8e9e --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepInstruction.h @@ -0,0 +1,61 @@ +//===-- 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 PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + ThreadPlanStepInstruction (Thread &thread, + bool step_over, + bool stop_others, + lldb::Vote stop_vote, + lldb::Vote run_vote); + +private: + friend ThreadPlan * + 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; + // This is used only for the step over case. + uint64_t m_stack_depth; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepInstruction); + +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepInstruction_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepOut.h b/lldb/include/lldb/Target/ThreadPlanStepOut.h new file mode 100644 index 000000000000..fd74e7199c30 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepOut.h @@ -0,0 +1,72 @@ +//===-- 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: + virtual ~ThreadPlanStepOut (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool WillResume (lldb::StateType resume_state, bool current_plan); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + ThreadPlanStepOut (Thread &thread, + SymbolContext *addr_context, + bool first_insn, + bool stop_others, + lldb::Vote stop_vote, + lldb::Vote run_vote); + +private: + SymbolContext *m_step_from_context; + lldb::addr_t m_step_from_insn; + uint64_t m_stack_depth; + lldb::break_id_t m_return_bp_id; + lldb::addr_t m_return_addr; + bool m_first_insn; + bool m_stop_others; + + friend ThreadPlan * + Thread::QueueThreadPlanForStepOut (bool abort_other_plans, + SymbolContext *addr_context, + bool first_insn, + bool stop_others, + lldb::Vote stop_vote, + lldb::Vote run_vote); + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOut); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOut_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h b/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h new file mode 100644 index 000000000000..14ba453ffd91 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepOverBreakpoint.h @@ -0,0 +1,55 @@ +//===-- 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 PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool IsImmediate () const + { + return false; + } + virtual bool WillResume (lldb::StateType resume_state, bool current_plan); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + +private: + + lldb::addr_t m_breakpoint_addr; + lldb::user_id_t m_breakpoint_site_id; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverBreakpoint); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOverBreakpoint_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepOverRange.h b/lldb/include/lldb/Target/ThreadPlanStepOverRange.h new file mode 100644 index 000000000000..9ecde535eb97 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepOverRange.h @@ -0,0 +1,56 @@ +//===-- 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: + virtual ~ThreadPlanStepOverRange (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ShouldStop (Event *event_ptr); + virtual bool + IsMasterPlan() + { + return true; + } + +protected: + + ThreadPlanStepOverRange (Thread &thread, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_others, bool okay_to_discard = false); + +private: + + friend ThreadPlan * + Thread::QueueThreadPlanForStepRange (bool abort_other_plans, + lldb::StepType type, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOverRange_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepRange.h b/lldb/include/lldb/Target/ThreadPlanStepRange.h new file mode 100644 index 000000000000..dfdd2e20e50e --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepRange.h @@ -0,0 +1,74 @@ +//===-- 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: + virtual ~ThreadPlanStepRange (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level) = 0; + virtual bool ValidatePlan (Stream *error); + virtual bool PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr) = 0; + virtual lldb::Vote ShouldReportStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + + ThreadPlanStepRange (const char *name, + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + bool InRange(); + bool FrameIsYounger(); + bool FrameIsOlder(); + bool InSymbol(); + + SymbolContext m_addr_context; + AddressRange m_address_range; + lldb::RunMode m_stop_others; + uint32_t m_stack_depth; + 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. + +private: + + // friend ThreadPlan * + // Thread::QueueThreadPlanForStepRange (bool abort_other_plans, StepType type, const AddressRange &range, SymbolContext *addr_context, bool stop_others); + + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepRange_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepThrough.h b/lldb/include/lldb/Target/ThreadPlanStepThrough.h new file mode 100644 index 000000000000..4763da719b85 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepThrough.h @@ -0,0 +1,58 @@ +//===-- 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 PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool WillResume (lldb::StateType resume_state, bool current_plan); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + ThreadPlanStepThrough (Thread &thread, + bool stop_others); + + bool + HappyToStopHere (); + +private: + friend ThreadPlan * + Thread::QueueThreadPlanForStepThrough (bool abort_other_plans, + bool stop_others); + + lldb::addr_t m_start_address; + bool m_stop_others; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepThrough); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepThrough_h_ diff --git a/lldb/include/lldb/Target/ThreadPlanStepUntil.h b/lldb/include/lldb/Target/ThreadPlanStepUntil.h new file mode 100644 index 000000000000..504fd2f8dfa8 --- /dev/null +++ b/lldb/include/lldb/Target/ThreadPlanStepUntil.h @@ -0,0 +1,83 @@ +//===-- 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 PlanExplainsStop (); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType RunState (); + virtual bool WillResume (lldb::StateType resume_state, bool current_plan); + virtual bool WillStop (); + virtual bool MischiefManaged (); + + virtual bool + IsMasterPlan() + { + return true; + } + +protected: + ThreadPlanStepUntil (Thread &thread, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others); + void AnalyzeStop(void); + +private: + + uint64_t m_stack_depth; + 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 ThreadPlan * + Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others); + + // 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/lldb/include/lldb/Target/UnixSignals.h b/lldb/include/lldb/Target/UnixSignals.h new file mode 100644 index 000000000000..c83a5a4d8adf --- /dev/null +++ b/lldb/include/lldb/Target/UnixSignals.h @@ -0,0 +1,168 @@ +//===-- 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, + bool default_suppress, + bool default_stop, + bool default_notify); + + void + RemoveSignal (int signo); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from UnixSignals can see and modify these + //------------------------------------------------------------------ + + struct Signal + { + typedef enum + { + eCondSuppress = 0, + eCondStop = 1, + eCondNotify + } Condition; + + ConstString m_name; + bool m_conditions[3]; + + Signal (const char *name, + bool default_suppress, + bool default_stop, + bool default_notify); + + ~Signal () {} + }; + + bool + GetCondition (int signo, + Signal::Condition cond_pos) const; + bool + SetCondition (int signo, + Signal::Condition cond_pos, + bool value); + + bool + SetCondition (const char *signal_name, + Signal::Condition cond_pos, + bool value); + + Signal * + GetSignalByName (const char *name, + int32_t &signo); + + const Signal * + GetSignalByName (const char *name, + int32_t &signo) const; + + void + Reset (); + +private: + //------------------------------------------------------------------ + // For UnixSignals only + //------------------------------------------------------------------ + typedef std::map collection; + + collection m_signals; + + DISALLOW_COPY_AND_ASSIGN (UnixSignals); +}; + +} // Namespace lldb +#endif // lldb_UnixSignals_h_ diff --git a/lldb/include/lldb/Target/Unwind.h b/lldb/include/lldb/Target/Unwind.h new file mode 100644 index 000000000000..f863add40dcc --- /dev/null +++ b/lldb/include/lldb/Target/Unwind.h @@ -0,0 +1,69 @@ +//===-- 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" + +namespace lldb_private { + +class Unwind +{ +protected: + //------------------------------------------------------------------ + // Classes that inherit from Unwind can see and modify these + //------------------------------------------------------------------ + Unwind(Thread &thread) : + m_thread (thread) + { + } + +public: + virtual + ~Unwind() + { + } + + virtual void + Clear() = 0; + + virtual uint32_t + GetFrameCount() = 0; + + virtual bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc) = 0; + + virtual RegisterContext * + CreateRegisterContextForFrame (StackFrame *frame) = 0; + + Thread & + GetThread() + { + return m_thread; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Unwind can see and modify these + //------------------------------------------------------------------ + Thread &m_thread; +private: + DISALLOW_COPY_AND_ASSIGN (Unwind); +}; + +} // namespace lldb_private + +#endif // liblldb_Unwind_h_ diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h new file mode 100644 index 000000000000..89d17af4ddf1 --- /dev/null +++ b/lldb/include/lldb/lldb-defines.h @@ -0,0 +1,88 @@ +//===-- 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_defines_h_ +#define LLDB_defines_h_ + +#include + +#if !defined(UINT32_MAX) + #define UINT32_MAX 4294967295U +#endif + +#if !defined(UINT64_MAX) + #define UINT64_MAX 18446744073709551615ULL +#endif + +//---------------------------------------------------------------------- +// lldb defines +//---------------------------------------------------------------------- +#define LLDB_GENERIC_ERROR ((uint32_t)UINT32_MAX) + +//---------------------------------------------------------------------- +// Breakpoints +//---------------------------------------------------------------------- +#define LLDB_INVALID_BREAK_ID ((lldb::break_id_t)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 ((lldb::user_id_t)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) + +//---------------------------------------------------------------------- +// 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 + +//---------------------------------------------------------------------- +/// Invalid value definitions +//---------------------------------------------------------------------- +#define LLDB_INVALID_ADDRESS (~((lldb::addr_t)0)) +#define LLDB_INVALID_INDEX32 ((uint32_t)UINT32_MAX) +#define LLDB_INVALID_REGNUM ((uint32_t)UINT32_MAX) +#define LLDB_INVALID_UID ((lldb::user_id_t)UINT32_MAX) +#define LLDB_INVALID_PROCESS_ID ((lldb::pid_t)0) +#define LLDB_INVALID_THREAD_ID ((lldb::tid_t)0) +#define LLDB_INVALID_FRAME_ID ((uint32_t) UINT32_MAX) +#define LLDB_INVALID_SIGNAL_NUMBER ((int32_t) INT32_MAX) + +//---------------------------------------------------------------------- +/// 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) + + + +#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_defines_h_ diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h new file mode 100644 index 000000000000..8ad33500ee42 --- /dev/null +++ b/lldb/include/lldb/lldb-enumerations.h @@ -0,0 +1,358 @@ +//===-- 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_enumerations_h_ +#define LLDB_enumerations_h_ + +namespace lldb { + +//---------------------------------------------------------------------- +// Process and Thread States +//---------------------------------------------------------------------- +typedef enum StateType +{ + eStateInvalid = 0, + eStateUnloaded, + eStateAttaching, + eStateLaunching, + eStateStopped, + eStateRunning, + eStateStepping, + eStateCrashed, + eStateDetached, + eStateExited, + eStateSuspended +} StateType; + +//---------------------------------------------------------------------- +// 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; + +//---------------------------------------------------------------------- +// Thread Run Modes +//---------------------------------------------------------------------- +typedef enum RunMode { + eOnlyThisThread, + eAllThreads, + eOnlyDuringStepping +} RunMode; + +//---------------------------------------------------------------------- +// 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; + +//---------------------------------------------------------------------- +// Byte ordering definitions +//---------------------------------------------------------------------- +typedef enum ByteOrder +{ + eByteOrderInvalid = 0, + eByteOrderLittle = 1234, + eByteOrderBig = 4321, + eByteOrderPDP = 3412, +#if defined (__LITTLE_ENDIAN__) + eByteOrderHost = eByteOrderLittle +#elif defined (__BIG_ENDIAN__) + eByteOrderHost = eByteOrderBig +#elif defined (__PDP_ENDIAN__) + eByteOrderHost = eByteOrderPDP +#else +#error unable to detect endianness +#endif +} 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, + eFormatCString, // NULL terminated C strings + eFormatDecimal, + eFormatEnum, + eFormatHex, + eFormatFloat, + eFormatOctal, + eFormatUnicode16, + eFormatUnicode32, + eFormatUnsigned, + eFormatPointer, + eFormatVectorOfChar, + eFormatVectorOfSInt8, + eFormatVectorOfUInt8, + eFormatVectorOfSInt16, + eFormatVectorOfUInt16, + eFormatVectorOfSInt32, + eFormatVectorOfUInt32, + eFormatVectorOfSInt64, + eFormatVectorOfUInt64, + eFormatVectorOfFloat32, + eFormatVectorOfFloat64, + eFormatVectorOfUInt128, + +} Format; + +//---------------------------------------------------------------------- +// Description levels for "void GetDescription(Stream *, DescriptionLevel)" calls +//---------------------------------------------------------------------- +typedef enum DescriptionLevel +{ + eDescriptionLevelBrief = 0, + eDescriptionLevelFull, + eDescriptionLevelVerbose, + kNumDescriptionLevels +} DescriptionLevel; + +//---------------------------------------------------------------------- +// Script interpreter types +//---------------------------------------------------------------------- +typedef enum ScriptLanguage +{ + eScriptLanguageNone, + eScriptLanguagePython, + eScriptLanguageDefault = eScriptLanguagePython +} ScriptLanguage; + +//---------------------------------------------------------------------- +// Register numbering types +//---------------------------------------------------------------------- +typedef enum RegisterKind +{ + eRegisterKindGCC = 0, + eRegisterKindDWARF, + eRegisterKindGeneric, + eRegisterKindGDB, + kNumRegisterKinds +} RegisterKind; + +//---------------------------------------------------------------------- +// Thread stop reasons +//---------------------------------------------------------------------- +typedef enum StopReason +{ + eStopReasonInvalid = 0, + eStopReasonNone, + eStopReasonTrace, + eStopReasonBreakpoint, + eStopReasonWatchpoint, + eStopReasonSignal, + eStopReasonException, + eStopReasonPlanComplete +} StopReason; + +//---------------------------------------------------------------------- +// Votes - Need a tri-state, yes, no, no opinion... +//---------------------------------------------------------------------- +typedef enum Vote +{ + eVoteNo = -1, + eVoteNoOpinion = 0, + eVoteYes = 1, +} Vote; + +//---------------------------------------------------------------------- +// Symbol types +//---------------------------------------------------------------------- +typedef enum SymbolType +{ + eSymbolTypeAny = 0, + eSymbolTypeInvalid = 0, + eSymbolTypeAbsolute, + eSymbolTypeExtern, + eSymbolTypeCode, + eSymbolTypeData, + eSymbolTypeTrampoline, + eSymbolTypeRuntime, + eSymbolTypeException, + eSymbolTypeSourceFile, + eSymbolTypeHeaderFile, + eSymbolTypeObjectFile, + eSymbolTypeFunction, + eSymbolTypeFunctionEnd, + eSymbolTypeCommonBlock, + eSymbolTypeBlock, + eSymbolTypeStatic, + eSymbolTypeGlobal, + 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 +} SymbolType; + + +//---------------------------------------------------------------------- +// Command Return Status Types +//---------------------------------------------------------------------- +typedef enum ReturnStatus +{ + eReturnStatusInvalid, + eReturnStatusSuccessFinishNoResult, + eReturnStatusSuccessFinishResult, + eReturnStatusSuccessContinuingNoResult, + eReturnStatusSuccessContinuingResult, + eReturnStatusStarted, + eReturnStatusFailed, + eReturnStatusQuit +} ReturnStatus; + + +//---------------------------------------------------------------------- +// Connection Status Types +//---------------------------------------------------------------------- +typedef enum ConnectionStatus +{ + eConnectionStatusSuccess, // Success + 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 +} 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 = (1 << 0), ///< Set when \a target is requested from a query, or was located in query results + eSymbolContextModule = (1 << 1), ///< Set when \a module is requested from a query, or was located in query results + eSymbolContextCompUnit = (1 << 2), ///< Set when \a comp_unit is requested from a query, or was located in query results + eSymbolContextFunction = (1 << 3), ///< Set when \a function is requested from a query, or was located in query results + eSymbolContextBlock = (1 << 4), ///< Set when the deepest \a block is requested from a query, or was located in query results + eSymbolContextLineEntry = (1 << 5), ///< Set when \a line_entry is requested from a query, or was located in query results + eSymbolContextSymbol = (1 << 6), ///< Set when \a symbol is requested from a query, or was located in query results + eSymbolContextEverything = ((eSymbolContextSymbol << 1) - 1) ///< Indicates to try and lookup everything up during a query. +} SymbolContextItem; + +typedef enum Permissions +{ + ePermissionsWritable = (1 << 0), + ePermissionsReadable = (1 << 1), + ePermissionsExecutable = (1 << 2) +}; + +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 + eSectionTypeOther + +} SectionType; + + +typedef enum InputReaderAction +{ + eInputReaderActivate, // reader is newly pushed onto the reader stack + 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) + eInputReaderDone, // reader was just popped off the stack and is done +} InputReaderAction; + +} // namespace lldb + + +#endif // LLDB_enumerations_h_ diff --git a/lldb/include/lldb/lldb-forward-rtti.h b/lldb/include/lldb/lldb-forward-rtti.h new file mode 100644 index 000000000000..163a319c9016 --- /dev/null +++ b/lldb/include/lldb/lldb-forward-rtti.h @@ -0,0 +1,76 @@ +//===-- lldb-forward-rtti.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_forward_rtti_h_ +#define LLDB_forward_rtti_h_ + +#if defined(__cplusplus) + +#ifndef NO_RTTI +//---------------------------------------------------------------------- +// And source files that may not have RTTI enabled during their +// compilation will want to do a "#define NO_RTTI" before including the +// lldb-include.h file. +//---------------------------------------------------------------------- + +#include +#include + +//---------------------------------------------------------------------- +// lldb forward declarations +//---------------------------------------------------------------------- +namespace lldb { + + typedef SharedPtr::Type ABISP; + typedef SharedPtr::Type AddressResolverSP; + typedef SharedPtr::Type BatonSP; + typedef SharedPtr::Type BlockSP; + typedef SharedPtr::Type BreakpointSP; + typedef SharedPtr::Type BreakpointSiteSP; + typedef SharedPtr::Type BreakpointLocationSP; + typedef SharedPtr::Type BreakpointResolverSP; + typedef SharedPtr::Type BroadcasterSP; + typedef SharedPtr::Type CommandObjectSP; + typedef SharedPtr::Type CommunicationSP; + typedef SharedPtr::Type CompUnitSP; + typedef SharedPtr::Type DataBufferSP; + typedef SharedPtr::Type DynamicLoaderSP; + typedef SharedPtr::Type EventSP; + typedef SharedPtr::Type FunctionSP; + typedef SharedPtr::Type InlineFunctionInfoSP; + typedef SharedPtr::Type InputReaderSP; + typedef SharedPtr::Type LineTableSP; + typedef SharedPtr::Type ListenerSP; + typedef SharedPtr::Type LogSP; + typedef SharedPtr::Type LogChannelSP; + typedef SharedPtr::Type ModuleSP; + typedef SharedPtr::Type ProcessSP; + typedef SharedPtr::Type RegisterContextSP; + typedef SharedPtr::Type SectionSP; + typedef SharedPtr::Type SearchFilterSP; + typedef SharedPtr::Type StackFrameSP; + typedef SharedPtr::Type StateVariableSP; + typedef SharedPtr::Type StoppointLocationSP; + typedef SharedPtr::Type StreamSP; + typedef SharedPtr::Type SymbolFileSP; + typedef SharedPtr::Type TargetSP; + typedef SharedPtr::Type ThreadSP; + typedef SharedPtr::Type ThreadPlanSP; + typedef SharedPtr::Type TypeSP; + typedef SharedPtr::Type ValueObjectSP; + typedef SharedPtr::Type VariableSP; + typedef SharedPtr::Type VariableListSP; + +} // namespace lldb + +#endif // #ifndef NO_RTTI + +#endif // #if defined(__cplusplus) + +#endif // LLDB_forward_rtti_h_ diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h new file mode 100644 index 000000000000..9e69ca09f52d --- /dev/null +++ b/lldb/include/lldb/lldb-forward.h @@ -0,0 +1,149 @@ +//===-- 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_forward_h_ +#define LLDB_forward_h_ + +#if defined(__cplusplus) + +//---------------------------------------------------------------------- +// lldb forward declarations +//---------------------------------------------------------------------- +namespace lldb_private { + +class ABI; +class Address; +class AddressRange; +class AddressResolver; +class ArchSpec; +class Args; +class Baton; +class Block; +class BlockList; +class Breakpoint; +class BreakpointID; +class BreakpointIDList; +class BreakpointSite; +class BreakpointSiteList; +class BreakpointList; +class BreakpointLocation; +class BreakpointLocationCollection; +class BreakpointLocationList; +class BreakpointOptions; +class BreakpointResolver; +class Broadcaster; +class ClangASTContext; +class ClangExpression; +class ClangExpressionDeclMap; +class ClangExpressionVariableList; +class CommandContext; +class CommandInterpreter; +class CommandObject; +class CommandReturnObject; +class Communication; +class Condition; +class CompileUnit; +class Connection; +class ConnectionFileDescriptor; +class ConstString; +class DWARFCallFrameInfo; +class DWARFExpression; +class DataBuffer; +class DataExtractor; +class Debugger; +class Declaration; +class Disassembler; +class DynamicLoader; +class Error; +class Event; +class EventData; +class ExecutionContext; +class ExecutionContextScope; +class FileSpec; +class FileSpecList; +class Flags; +class Function; +class FunctionInfo; +class InlineFunctionInfo; +class InputReader; +struct LineEntry; +class LineTable; +class Listener; +class Log; +class LogChannel; +class Mangled; +class Module; +class ModuleList; +class Mutex; +class ObjCObjectPrinter; +class ObjectContainer; +class ObjectFile; +class Options; +class PathMappingList; +class Process; +class RegisterContext; +class RegisterLocation; +class RegisterLocationList; +class RegularExpression; +class Scalar; +class ScriptInterpreter; +class ScriptInterpreterPython; +class SearchFilter; +class Section; +class SectionList; +class SourceManager; +class StackFrame; +class StackFrameList; +class StackID; +class StateVariable; +class Stoppoint; +class StoppointCallbackContext; +class StoppointLocation; +class Stream; +class StreamFile; +class StreamString; +class StringList; +class Symbol; +class SymbolContext; +class SymbolContextList; +class SymbolContextScope; +class SymbolFile; +class SymbolVendor; +class Symtab; +class Target; +class TargetList; +class Thread; +class ThreadList; +class ThreadPlan; +class ThreadPlanContinue; +class ThreadPlanBase; +class ThreadPlanStepInstruction; +class ThreadPlanStepOut; +class ThreadPlanStepOverBreakpoint; +class ThreadPlanStepThrough; +class ThreadPlanStepRange; +class ThreadPlanRunToAddress; +class TimeValue; +class Type; +class TypeList; +class Unwind; +class UUID; +class VMRange; +class Value; +class ValueList; +class ValueObject; +class ValueObjectList; +class Variable; +class VariableList; +class WatchpointLocation; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // LLDB_forward_h_ diff --git a/lldb/include/lldb/lldb-include.h b/lldb/include/lldb/lldb-include.h new file mode 100644 index 000000000000..de415c48ad9a --- /dev/null +++ b/lldb/include/lldb/lldb-include.h @@ -0,0 +1,19 @@ +//===-- 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_include_h_ +#define LLDB_include_h_ + +#include +#include +#include +#include +#include + +#endif // LLDB_include_h_ diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h new file mode 100644 index 000000000000..3cd8aab476bc --- /dev/null +++ b/lldb/include/lldb/lldb-private-interfaces.h @@ -0,0 +1,37 @@ +//===-- 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_interfaces_h_ +#define liblldb_lldb_interfaces_h_ + +#if defined(__cplusplus) +#ifndef NO_RTTI + +#include "lldb/lldb-private.h" + +namespace lldb_private +{ + typedef ABI* (*ABICreateInstance) (const ConstString &triple); + typedef Disassembler* (*DisassemblerCreateInstance) (const ArchSpec &arch); + typedef DynamicLoader* (*DynamicLoaderCreateInstance) (Process* process); + typedef ObjectContainer* (*ObjectContainerCreateInstance) (Module* module, lldb::DataBufferSP& dataSP, const FileSpec *file, lldb::addr_t offset, lldb::addr_t length); + typedef ObjectFile* (*ObjectFileCreateInstance) (Module* module, lldb::DataBufferSP& dataSP, const FileSpec* file, lldb::addr_t offset, lldb::addr_t length); + typedef LogChannel* (*LogChannelCreateInstance) (); + typedef Process* (*ProcessCreateInstance) (Target &target, Listener &listener); + typedef SymbolFile* (*SymbolFileCreateInstance) (ObjectFile* obj_file); + typedef SymbolVendor* (*SymbolVendorCreateInstance) (Module *module); // 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, uint32_t type); + typedef ThreadPlan * (*ThreadPlanShouldStopHereCallback) (ThreadPlan *current_plan, Flags &flags, void *baton); +} // namespace lldb_private + +#endif // #ifndef NO_RTTI +#endif // #if defined(__cplusplus) + +#endif // liblldb_lldb_interfaces_h_ diff --git a/lldb/include/lldb/lldb-private-log.h b/lldb/include/lldb/lldb-private-log.h new file mode 100644 index 000000000000..394fe73e2698 --- /dev/null +++ b/lldb/include/lldb/lldb-private-log.h @@ -0,0 +1,83 @@ +//===-- 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_log_h_ +#define liblldb_lldb_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_SHLIB (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_ALL (UINT32_MAX) +#define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\ + LIBLLDB_LOG_THREAD |\ + LIBLLDB_LOG_SHLIB |\ + LIBLLDB_LOG_BREAKPOINTS |\ + LIBLLDB_LOG_WATCHPOINTS |\ + LIBLLDB_LOG_STEP |\ + LIBLLDB_LOG_STATE ) + +namespace lldb_private { + +uint32_t +GetLogMask (); + +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 (); + +#ifndef NO_RTTI + +Log * +EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm); + +#endif + +void +ListLogCategories (Stream *strm); + +} // namespace lldb_private + +#endif // liblldb_lldb_log_h_ diff --git a/lldb/include/lldb/lldb-private.h b/lldb/include/lldb/lldb-private.h new file mode 100644 index 000000000000..a67ebf27c141 --- /dev/null +++ b/lldb/include/lldb/lldb-private.h @@ -0,0 +1,77 @@ +//===-- 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-include.h" +#include "lldb/lldb-private-interfaces.h" +#include "lldb/lldb-private-log.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 (); + +// The function below can be moved into lldb::Debugger when/if we get one +ArchSpec & +GetDefaultArchitecture (); + +} // namespace lldb_private + + +#endif // defined(__cplusplus) + + +#endif // lldb_lldb_private_h_ diff --git a/lldb/include/lldb/lldb-types.h b/lldb/include/lldb/lldb-types.h new file mode 100644 index 000000000000..c83a3861fd9f --- /dev/null +++ b/lldb/include/lldb/lldb-types.h @@ -0,0 +1,168 @@ +//===-- 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_types_h_ +#define LLDB_types_h_ + +#include +#include + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// MACOSX START +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_RTTI + +//---------------------------------------------------------------------- +// And source files that may not have RTTI enabled during their +// compilation will want to do a "#define NO_RTTI" before including the +// lldb-include.h file. +//---------------------------------------------------------------------- + +#include // for std::tr1::shared_ptr + +#endif + +//---------------------------------------------------------------------- +// All host systems must define: +// liblldb::condition_t The native condition type (or a substitute class) for conditions on the host system. +// liblldb::mutex_t The native mutex type for mutex objects on the host system. +// liblldb::thread_t The native thread type for spawned threads on the system +// liblldb::thread_arg_t The type of the one any only thread creation argument for the host system +// liblldb::thread_result_t The return type that gets returned when a thread finishes. +// liblldb::thread_func_t The function prototype used to spawn a thread on the host system. +// liblldb::SharedPtr The template that wraps up the host version of a reference counted pointer (like boost::shared_ptr) +// #define LLDB_INVALID_PROCESS_ID ... +// #define LLDB_INVALID_THREAD_ID ... +// #define LLDB_INVALID_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 + +#ifndef NO_RTTI + // The template below can be used in a few useful ways: + // + // // Make a single shared pointer a class Foo + // lldb::SharePtr::Type foo_sp; + // + // // Make a typedef to a Foo shared pointer + // typedef lldb::SharePtr::Type FooSP; + // + template + struct SharedPtr + { + typedef std::tr1::shared_ptr<_Tp> Type; + }; +#endif + +} // namespace lldb + +#define LLDB_INVALID_HOST_THREAD ((lldb::thread_t)NULL) +#define LLDB_INVALID_HOST_TIME { 0, 0 } + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// MACOSX END +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +#ifdef SWIG +#define CONST_CHAR_PTR char * +#else +#define CONST_CHAR_PTR const char * +#endif + +namespace lldb { + typedef uint64_t addr_t; + typedef uint32_t user_id_t; + typedef int32_t pid_t; + typedef uint32_t tid_t; + typedef int32_t break_id_t; + + //---------------------------------------------------------------------- + // 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_PTR name; // Name of this register, can't be NULL + CONST_CHAR_PTR 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 reg; // The native register number for this register + uint32_t kinds[kNumRegisterKinds]; // Holds all of the various register numbers for all register kinds + } RegisterInfo; + + //---------------------------------------------------------------------- + // Registers are grouped into register sets + //---------------------------------------------------------------------- + typedef struct + { + CONST_CHAR_PTR name; // Name of this register set + CONST_CHAR_PTR 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 + { + int value; + CONST_CHAR_PTR string_value; + CONST_CHAR_PTR usage; + } OptionEnumValueElement; + + typedef struct + { + uint32_t usage_level; // Used to mark options that can be used together. + bool required; // This option is required (in the current usage level) + CONST_CHAR_PTR long_option; // Full name for this option. + char 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 completionType; // Cookie the option class can use to do define the argument completion. + CONST_CHAR_PTR argument_name; // Text name to be use in usage text to refer to the option's value. + CONST_CHAR_PTR usage_text; // Full text explaining what this options does and what (if any) argument to + // pass it. + } OptionDefinition; + + + typedef int (*comparison_function)(const void *, const void *); +} + +#undef CONST_CHAR_PTR + +#endif // LLDB_types_h_ diff --git a/lldb/lldb.runcontext b/lldb/lldb.runcontext new file mode 100644 index 000000000000..6a1aaecb51aa --- /dev/null +++ b/lldb/lldb.runcontext @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..c5a9c5b4e7f0 --- /dev/null +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -0,0 +1,3020 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 260C876A10F538E700BB2B04 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 260C876910F538E700BB2B04 /* Foundation.framework */; }; + 261744781168585B005ADD65 /* SBType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 261744771168585B005ADD65 /* SBType.cpp */; }; + 2617447A11685869005ADD65 /* SBType.h in Headers */ = {isa = PBXBuildFile; fileRef = 2617447911685869005ADD65 /* SBType.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 262CFC7711A4510000946C6C /* debugserver in Resources */ = {isa = PBXBuildFile; fileRef = 26CE05A0115C31E50022F371 /* debugserver */; }; + 265ABF6310F42EE900531910 /* DebugSymbols.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 265ABF6210F42EE900531910 /* DebugSymbols.framework */; }; + 2668020E115FD12C008E1FE4 /* lldb-defines.h in Headers */ = {isa = PBXBuildFile; fileRef = 26BC7C2510F1B3BC00F91463 /* lldb-defines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668020F115FD12C008E1FE4 /* lldb-enumerations.h in Headers */ = {isa = PBXBuildFile; fileRef = 26BC7C2610F1B3BC00F91463 /* lldb-enumerations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680214115FD12C008E1FE4 /* lldb-types.h in Headers */ = {isa = PBXBuildFile; fileRef = 26BC7C2910F1B3BC00F91463 /* lldb-types.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680219115FD13D008E1FE4 /* SBBreakpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AF16A9E11402D69007A7B3F /* SBBreakpoint.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668021A115FD13D008E1FE4 /* SBBreakpointLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AF16CC611408686007A7B3F /* SBBreakpointLocation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668021B115FD13D008E1FE4 /* SBBroadcaster.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830F31125FC5800A56CB0 /* SBBroadcaster.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668021C115FD13D008E1FE4 /* SBCommandContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830F51125FC5800A56CB0 /* SBCommandContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668021D115FD13D008E1FE4 /* SBCommandInterpreter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830F71125FC5800A56CB0 /* SBCommandInterpreter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668021E115FD13D008E1FE4 /* SBCommandReturnObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830F91125FC5800A56CB0 /* SBCommandReturnObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668021F115FD13D008E1FE4 /* SBCommunication.h in Headers */ = {isa = PBXBuildFile; fileRef = 260223E7115F06D500A601A2 /* SBCommunication.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680220115FD13D008E1FE4 /* SBDebugger.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830FB1125FC5800A56CB0 /* SBDebugger.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680221115FD13D008E1FE4 /* SBDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830FC1125FC5800A56CB0 /* SBDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680222115FD13D008E1FE4 /* SBError.h in Headers */ = {isa = PBXBuildFile; fileRef = 2682F286115EF3BD00CCFF99 /* SBError.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680223115FD13D008E1FE4 /* SBEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9830FE1125FC5800A56CB0 /* SBEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680224115FD13D008E1FE4 /* SBFileSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 26022531115F27FA00A601A2 /* SBFileSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680225115FD13D008E1FE4 /* SBFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A633FE8112DCE3C001A7E43 /* SBFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26680227115FD13D008E1FE4 /* SBListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9831021125FC5800A56CB0 /* SBListener.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668022A115FD13D008E1FE4 /* SBProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9831041125FC5800A56CB0 /* SBProcess.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668022B115FD13D008E1FE4 /* SBSourceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9831061125FC5800A56CB0 /* SBSourceManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668022C115FD13D008E1FE4 /* SBTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A9831081125FC5800A56CB0 /* SBTarget.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668022E115FD13D008E1FE4 /* SBThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A98310A1125FC5800A56CB0 /* SBThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2668022F115FD19D008E1FE4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C39010F3FA26009D5894 /* CoreFoundation.framework */; }; + 26680230115FD19E008E1FE4 /* DebugSymbols.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 265ABF6210F42EE900531910 /* DebugSymbols.framework */; }; + 26680231115FD1A0008E1FE4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 260C876910F538E700BB2B04 /* Foundation.framework */; }; + 26680232115FD1A4008E1FE4 /* libpython2.6.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32410F3DF23009D5894 /* libpython2.6.dylib */; }; + 26680233115FD1A7008E1FE4 /* libobjc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C37410F3F61B009D5894 /* libobjc.dylib */; }; + 26680324116005D9008E1FE4 /* SBThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9831091125FC5800A56CB0 /* SBThread.cpp */; }; + 26680326116005DB008E1FE4 /* SBTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9831071125FC5800A56CB0 /* SBTarget.cpp */; }; + 26680327116005DC008E1FE4 /* SBSourceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9831051125FC5800A56CB0 /* SBSourceManager.cpp */; }; + 26680328116005DE008E1FE4 /* SBProcess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9831031125FC5800A56CB0 /* SBProcess.cpp */; }; + 2668032A116005E0008E1FE4 /* SBListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9831011125FC5800A56CB0 /* SBListener.cpp */; }; + 2668032C116005E2008E1FE4 /* SBFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A633FE7112DCE3C001A7E43 /* SBFrame.cpp */; }; + 2668032D116005E3008E1FE4 /* SBFileSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26022532115F281400A601A2 /* SBFileSpec.cpp */; }; + 2668032E116005E5008E1FE4 /* SBEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9830FD1125FC5800A56CB0 /* SBEvent.cpp */; }; + 2668032F116005E6008E1FE4 /* SBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2682F284115EF3A700CCFF99 /* SBError.cpp */; }; + 26680330116005E7008E1FE4 /* SBDebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9830FA1125FC5800A56CB0 /* SBDebugger.cpp */; }; + 26680331116005E9008E1FE4 /* SBCommunication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260223E8115F06E500A601A2 /* SBCommunication.cpp */; }; + 26680332116005EA008E1FE4 /* SBCommandReturnObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9830F81125FC5800A56CB0 /* SBCommandReturnObject.cpp */; }; + 26680333116005EC008E1FE4 /* SBCommandInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9830F61125FC5800A56CB0 /* SBCommandInterpreter.cpp */; }; + 26680334116005ED008E1FE4 /* SBCommandContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9830F41125FC5800A56CB0 /* SBCommandContext.cpp */; }; + 26680335116005EE008E1FE4 /* SBBroadcaster.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A9830F21125FC5800A56CB0 /* SBBroadcaster.cpp */; }; + 26680336116005EF008E1FE4 /* SBBreakpointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */; }; + 26680337116005F1008E1FE4 /* SBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AF16A9C11402D5B007A7B3F /* SBBreakpoint.cpp */; }; + 2668035C11601108008E1FE4 /* LLDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26680207115FD0ED008E1FE4 /* LLDB.framework */; }; + 26B42B1F1187A92B0079C8C8 /* lldb-include.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B42B1E1187A92B0079C8C8 /* lldb-include.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26B42C4D1187ABA50079C8C8 /* LLDB.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B42C4C1187ABA50079C8C8 /* LLDB.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26D5B06511B07550009A862E /* StoppointCallbackContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0910F1B83100F91463 /* StoppointCallbackContext.cpp */; }; + 26D5B06611B07550009A862E /* Breakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0A10F1B83100F91463 /* Breakpoint.cpp */; }; + 26D5B06711B07550009A862E /* BreakpointID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0B10F1B83100F91463 /* BreakpointID.cpp */; }; + 26D5B06811B07550009A862E /* BreakpointIDList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0C10F1B83100F91463 /* BreakpointIDList.cpp */; }; + 26D5B06911B07550009A862E /* BreakpointList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0D10F1B83100F91463 /* BreakpointList.cpp */; }; + 26D5B06A11B07550009A862E /* BreakpointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0E10F1B83100F91463 /* BreakpointLocation.cpp */; }; + 26D5B06B11B07550009A862E /* BreakpointLocationCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E0F10F1B83100F91463 /* BreakpointLocationCollection.cpp */; }; + 26D5B06C11B07550009A862E /* BreakpointLocationList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1010F1B83100F91463 /* BreakpointLocationList.cpp */; }; + 26D5B06D11B07550009A862E /* BreakpointOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1110F1B83100F91463 /* BreakpointOptions.cpp */; }; + 26D5B06E11B07550009A862E /* BreakpointResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1210F1B83100F91463 /* BreakpointResolver.cpp */; }; + 26D5B06F11B07550009A862E /* BreakpointSite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1310F1B83100F91463 /* BreakpointSite.cpp */; }; + 26D5B07011B07550009A862E /* BreakpointSiteList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1410F1B83100F91463 /* BreakpointSiteList.cpp */; }; + 26D5B07111B07550009A862E /* SearchFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1510F1B83100F91463 /* SearchFilter.cpp */; }; + 26D5B07211B07550009A862E /* Stoppoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1610F1B83100F91463 /* Stoppoint.cpp */; }; + 26D5B07311B07550009A862E /* StoppointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1710F1B83100F91463 /* StoppointLocation.cpp */; }; + 26D5B07411B07550009A862E /* WatchpointLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E1810F1B83100F91463 /* WatchpointLocation.cpp */; }; + 26D5B07511B07550009A862E /* CommandObjectAlias.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E2A10F1B84700F91463 /* CommandObjectAlias.cpp */; }; + 26D5B07611B07550009A862E /* CommandObjectAppend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E2B10F1B84700F91463 /* CommandObjectAppend.cpp */; }; + 26D5B07711B07550009A862E /* CommandObjectBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E2D10F1B84700F91463 /* CommandObjectBreakpoint.cpp */; }; + 26D5B07811B07550009A862E /* CommandObjectDelete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E2F10F1B84700F91463 /* CommandObjectDelete.cpp */; }; + 26D5B07911B07550009A862E /* CommandObjectDisassemble.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3010F1B84700F91463 /* CommandObjectDisassemble.cpp */; }; + 26D5B07A11B07550009A862E /* CommandObjectExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3110F1B84700F91463 /* CommandObjectExpression.cpp */; }; + 26D5B07B11B07550009A862E /* CommandObjectFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3210F1B84700F91463 /* CommandObjectFile.cpp */; }; + 26D5B07C11B07550009A862E /* CommandObjectHelp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3310F1B84700F91463 /* CommandObjectHelp.cpp */; }; + 26D5B07D11B07550009A862E /* CommandObjectImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3410F1B84700F91463 /* CommandObjectImage.cpp */; }; + 26D5B07E11B07550009A862E /* CommandObjectInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3510F1B84700F91463 /* CommandObjectInfo.cpp */; }; + 26D5B07F11B07550009A862E /* CommandObjectMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3610F1B84700F91463 /* CommandObjectMemory.cpp */; }; + 26D5B08011B07550009A862E /* CommandObjectProcess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3810F1B84700F91463 /* CommandObjectProcess.cpp */; }; + 26D5B08111B07550009A862E /* CommandObjectQuit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3910F1B84700F91463 /* CommandObjectQuit.cpp */; }; + 26D5B08211B07550009A862E /* CommandObjectRegister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3B10F1B84700F91463 /* CommandObjectRegister.cpp */; }; + 26D5B08311B07550009A862E /* CommandObjectScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3D10F1B84700F91463 /* CommandObjectScript.cpp */; }; + 26D5B08411B07550009A862E /* CommandObjectSelect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3E10F1B84700F91463 /* CommandObjectSelect.cpp */; }; + 26D5B08511B07550009A862E /* CommandObjectSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E3F10F1B84700F91463 /* CommandObjectSet.cpp */; }; + 26D5B08611B07550009A862E /* CommandObjectSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4010F1B84700F91463 /* CommandObjectSettings.cpp */; }; + 26D5B08711B07550009A862E /* CommandObjectShow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4110F1B84700F91463 /* CommandObjectShow.cpp */; }; + 26D5B08811B07550009A862E /* CommandObjectSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4210F1B84700F91463 /* CommandObjectSource.cpp */; }; + 26D5B08911B07550009A862E /* CommandObjectSourceFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4310F1B84700F91463 /* CommandObjectSourceFile.cpp */; }; + 26D5B08A11B07550009A862E /* CommandObjectStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4410F1B84700F91463 /* CommandObjectStatus.cpp */; }; + 26D5B08B11B07550009A862E /* CommandObjectSyntax.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4510F1B84700F91463 /* CommandObjectSyntax.cpp */; }; + 26D5B08C11B07550009A862E /* CommandObjectThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4610F1B84700F91463 /* CommandObjectThread.cpp */; }; + 26D5B08D11B07550009A862E /* CommandObjectVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E4810F1B84700F91463 /* CommandObjectVariable.cpp */; }; + 26D5B08E11B07550009A862E /* Address.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6910F1B85900F91463 /* Address.cpp */; }; + 26D5B08F11B07550009A862E /* AddressRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6A10F1B85900F91463 /* AddressRange.cpp */; }; + 26D5B09011B07550009A862E /* ArchSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6B10F1B85900F91463 /* ArchSpec.cpp */; }; + 26D5B09111B07550009A862E /* Args.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6C10F1B85900F91463 /* Args.cpp */; }; + 26D5B09211B07550009A862E /* Broadcaster.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6D10F1B85900F91463 /* Broadcaster.cpp */; }; + 26D5B09311B07550009A862E /* Communication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6E10F1B85900F91463 /* Communication.cpp */; }; + 26D5B09411B07550009A862E /* Connection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6F10F1B85900F91463 /* Connection.cpp */; }; + 26D5B09511B07550009A862E /* ConnectionFileDescriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7010F1B85900F91463 /* ConnectionFileDescriptor.cpp */; }; + 26D5B09611B07550009A862E /* DataExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7110F1B85900F91463 /* DataExtractor.cpp */; }; + 26D5B09711B07550009A862E /* DataBufferHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */; }; + 26D5B09811B07550009A862E /* DataBufferMemoryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */; }; + 26D5B09911B07550009A862E /* lldb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7410F1B85900F91463 /* lldb.cpp */; }; + 26D5B09A11B07550009A862E /* lldb-log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7510F1B85900F91463 /* lldb-log.cpp */; }; + 26D5B09B11B07550009A862E /* Disassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7610F1B85900F91463 /* Disassembler.cpp */; }; + 26D5B09C11B07550009A862E /* DynamicLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7710F1B85900F91463 /* DynamicLoader.cpp */; }; + 26D5B09D11B07550009A862E /* Error.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7810F1B85900F91463 /* Error.cpp */; }; + 26D5B09E11B07550009A862E /* Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7910F1B85900F91463 /* Event.cpp */; }; + 26D5B09F11B07550009A862E /* FileSpec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7A10F1B85900F91463 /* FileSpec.cpp */; }; + 26D5B0A011B07550009A862E /* FileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7B10F1B85900F91463 /* FileSpecList.cpp */; }; + 26D5B0A111B07550009A862E /* Flags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7C10F1B85900F91463 /* Flags.cpp */; }; + 26D5B0A211B07550009A862E /* Language.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7D10F1B85900F91463 /* Language.cpp */; }; + 26D5B0A311B07550009A862E /* Listener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7E10F1B85900F91463 /* Listener.cpp */; }; + 26D5B0A411B07550009A862E /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7F10F1B85900F91463 /* Log.cpp */; }; + 26D5B0A511B07550009A862E /* Mangled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8010F1B85900F91463 /* Mangled.cpp */; }; + 26D5B0A611B07550009A862E /* Module.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8110F1B85900F91463 /* Module.cpp */; }; + 26D5B0A711B07550009A862E /* ModuleChild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8210F1B85900F91463 /* ModuleChild.cpp */; }; + 26D5B0A811B07550009A862E /* ModuleList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8310F1B85900F91463 /* ModuleList.cpp */; }; + 26D5B0A911B07550009A862E /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8610F1B85900F91463 /* Options.cpp */; }; + 26D5B0AA11B07550009A862E /* PluginManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8A10F1B85900F91463 /* PluginManager.cpp */; }; + 26D5B0AB11B07550009A862E /* RegularExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8C10F1B85900F91463 /* RegularExpression.cpp */; }; + 26D5B0AC11B07550009A862E /* Scalar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8D10F1B85900F91463 /* Scalar.cpp */; }; + 26D5B0AD11B07550009A862E /* Section.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8E10F1B85900F91463 /* Section.cpp */; }; + 26D5B0AE11B07550009A862E /* SourceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8F10F1B85900F91463 /* SourceManager.cpp */; }; + 26D5B0AF11B07550009A862E /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9010F1B85900F91463 /* State.cpp */; }; + 26D5B0B011B07550009A862E /* Stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9110F1B85900F91463 /* Stream.cpp */; }; + 26D5B0B111B07550009A862E /* StreamFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9210F1B85900F91463 /* StreamFile.cpp */; }; + 26D5B0B211B07550009A862E /* StreamString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9310F1B85900F91463 /* StreamString.cpp */; }; + 26D5B0B311B07550009A862E /* ConstString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9410F1B85900F91463 /* ConstString.cpp */; }; + 26D5B0B411B07550009A862E /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9610F1B85900F91463 /* Timer.cpp */; }; + 26D5B0B511B07550009A862E /* TTYState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9710F1B85900F91463 /* TTYState.cpp */; }; + 26D5B0B611B07550009A862E /* UserID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9810F1B85900F91463 /* UserID.cpp */; }; + 26D5B0B711B07550009A862E /* Value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9910F1B85900F91463 /* Value.cpp */; }; + 26D5B0B811B07550009A862E /* ValueObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */; }; + 26D5B0B911B07550009A862E /* ValueObjectChild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */; }; + 26D5B0BA11B07550009A862E /* ValueObjectList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */; }; + 26D5B0BB11B07550009A862E /* ValueObjectVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */; }; + 26D5B0BC11B07550009A862E /* VMRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9E10F1B85900F91463 /* VMRange.cpp */; }; + 26D5B0BD11B07550009A862E /* ClangExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7ED510F1B86700F91463 /* ClangExpression.cpp */; }; + 26D5B0BE11B07550009A862E /* ClangExpressionVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7ED610F1B86700F91463 /* ClangExpressionVariable.cpp */; }; + 26D5B0BF11B07550009A862E /* ClangStmtVisitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7ED710F1B86700F91463 /* ClangStmtVisitor.cpp */; }; + 26D5B0C011B07550009A862E /* DWARFExpression.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7ED810F1B86700F91463 /* DWARFExpression.cpp */; }; + 26D5B0C111B07550009A862E /* Condition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EE710F1B88F00F91463 /* Condition.cpp */; }; + 26D5B0C211B07550009A862E /* Host.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EE810F1B88F00F91463 /* Host.mm */; }; + 26D5B0C311B07550009A862E /* Mutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EE910F1B88F00F91463 /* Mutex.cpp */; }; + 26D5B0C411B07550009A862E /* CFCBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EED10F1B8AD00F91463 /* CFCBundle.cpp */; }; + 26D5B0C511B07550009A862E /* CFCData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EEF10F1B8AD00F91463 /* CFCData.cpp */; }; + 26D5B0C611B07550009A862E /* CFCMutableArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF110F1B8AD00F91463 /* CFCMutableArray.cpp */; }; + 26D5B0C711B07550009A862E /* CFCMutableDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF310F1B8AD00F91463 /* CFCMutableDictionary.cpp */; }; + 26D5B0C811B07550009A862E /* CFCMutableSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF510F1B8AD00F91463 /* CFCMutableSet.cpp */; }; + 26D5B0C911B07550009A862E /* CFCString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7EF810F1B8AD00F91463 /* CFCString.cpp */; }; + 26D5B0CA11B07550009A862E /* CommandContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0710F1B8DD00F91463 /* CommandContext.cpp */; }; + 26D5B0CB11B07550009A862E /* CommandInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0810F1B8DD00F91463 /* CommandInterpreter.cpp */; }; + 26D5B0CC11B07550009A862E /* CommandObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0910F1B8DD00F91463 /* CommandObject.cpp */; }; + 26D5B0CD11B07550009A862E /* CommandReturnObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0A10F1B8DD00F91463 /* CommandReturnObject.cpp */; }; + 26D5B0CE11B07550009A862E /* StateVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0B10F1B8DD00F91463 /* StateVariable.cpp */; }; + 26D5B0CF11B07550009A862E /* ScriptInterpreterPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F0C10F1B8DD00F91463 /* ScriptInterpreterPython.cpp */; }; + 26D5B0D011B07550009A862E /* Block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1310F1B8EC00F91463 /* Block.cpp */; }; + 26D5B0D111B07550009A862E /* ClangASTContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1410F1B8EC00F91463 /* ClangASTContext.cpp */; settings = {COMPILER_FLAGS = "-fno-rtti"; }; }; + 26D5B0D211B07550009A862E /* CompileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1510F1B8EC00F91463 /* CompileUnit.cpp */; }; + 26D5B0D311B07550009A862E /* Declaration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1610F1B8EC00F91463 /* Declaration.cpp */; }; + 26D5B0D411B07550009A862E /* DWARFCallFrameInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1710F1B8EC00F91463 /* DWARFCallFrameInfo.cpp */; }; + 26D5B0D511B07550009A862E /* Function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1810F1B8EC00F91463 /* Function.cpp */; }; + 26D5B0D611B07550009A862E /* LineEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1910F1B8EC00F91463 /* LineEntry.cpp */; }; + 26D5B0D711B07550009A862E /* LineTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */; }; + 26D5B0D811B07550009A862E /* Symbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1B10F1B8EC00F91463 /* Symbol.cpp */; }; + 26D5B0D911B07550009A862E /* SymbolContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1C10F1B8EC00F91463 /* SymbolContext.cpp */; }; + 26D5B0DA11B07550009A862E /* SymbolFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1D10F1B8EC00F91463 /* SymbolFile.cpp */; }; + 26D5B0DB11B07550009A862E /* SymbolVendor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1E10F1B8EC00F91463 /* SymbolVendor.mm */; }; + 26D5B0DC11B07550009A862E /* Symtab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1F10F1B8EC00F91463 /* Symtab.cpp */; }; + 26D5B0DD11B07550009A862E /* Type.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2010F1B8EC00F91463 /* Type.cpp */; }; + 26D5B0DE11B07550009A862E /* TypeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2110F1B8EC00F91463 /* TypeList.cpp */; }; + 26D5B0DF11B07550009A862E /* Variable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2210F1B8EC00F91463 /* Variable.cpp */; }; + 26D5B0E011B07550009A862E /* VariableList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F2310F1B8EC00F91463 /* VariableList.cpp */; }; + 26D5B0E111B07550009A862E /* ExecutionContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3510F1B90C00F91463 /* ExecutionContext.cpp */; }; + 26D5B0E211B07550009A862E /* Process.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3610F1B90C00F91463 /* Process.cpp */; }; + 26D5B0E311B07550009A862E /* RegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3710F1B90C00F91463 /* RegisterContext.cpp */; }; + 26D5B0E411B07550009A862E /* StackFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3810F1B90C00F91463 /* StackFrame.cpp */; }; + 26D5B0E511B07550009A862E /* StackFrameList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */; }; + 26D5B0E611B07550009A862E /* StackID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3A10F1B90C00F91463 /* StackID.cpp */; }; + 26D5B0E711B07550009A862E /* Target.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3B10F1B90C00F91463 /* Target.cpp */; }; + 26D5B0E811B07550009A862E /* TargetList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3C10F1B90C00F91463 /* TargetList.cpp */; }; + 26D5B0E911B07550009A862E /* Thread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3D10F1B90C00F91463 /* Thread.cpp */; }; + 26D5B0EA11B07550009A862E /* ThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3E10F1B90C00F91463 /* ThreadList.cpp */; }; + 26D5B0EB11B07550009A862E /* ThreadPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3F10F1B90C00F91463 /* ThreadPlan.cpp */; }; + 26D5B0EC11B07550009A862E /* ObjectFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F4C10F1BC1A00F91463 /* ObjectFile.cpp */; }; + 26D5B0ED11B07550009A862E /* ThreadPlanContinue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847010F50EFC00BB2B04 /* ThreadPlanContinue.cpp */; }; + 26D5B0EE11B07550009A862E /* ThreadPlanBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847110F50EFC00BB2B04 /* ThreadPlanBase.cpp */; }; + 26D5B0EF11B07550009A862E /* ThreadPlanStepInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847210F50EFC00BB2B04 /* ThreadPlanStepInstruction.cpp */; }; + 26D5B0F011B07550009A862E /* ThreadPlanStepOut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847310F50EFC00BB2B04 /* ThreadPlanStepOut.cpp */; }; + 26D5B0F111B07550009A862E /* ThreadPlanStepOverBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847410F50EFC00BB2B04 /* ThreadPlanStepOverBreakpoint.cpp */; }; + 26D5B0F211B07550009A862E /* ThreadPlanStepThrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847510F50EFC00BB2B04 /* ThreadPlanStepThrough.cpp */; }; + 26D5B0F311B07550009A862E /* ThreadPlanStepRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C847610F50EFC00BB2B04 /* ThreadPlanStepRange.cpp */; }; + 26D5B0F411B07550009A862E /* DynamicLoaderMacOSXDYLD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C897A10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.cpp */; }; + 26D5B0F511B07550009A862E /* DynamicLoaderMacOSXDYLDLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C897C10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLDLog.cpp */; }; + 26D5B0F611B07550009A862E /* ObjectContainerUniversalMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C898010F57C5600BB2B04 /* ObjectContainerUniversalMachO.cpp */; }; + 26D5B0F711B07550009A862E /* ObjectFileELF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C898510F57C5600BB2B04 /* ObjectFileELF.cpp */; }; + 26D5B0F811B07550009A862E /* ObjectFileMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C898810F57C5600BB2B04 /* ObjectFileMachO.cpp */; }; + 26D5B0F911B07550009A862E /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899210F57C5600BB2B04 /* MachException.cpp */; }; + 26D5B0FA11B07550009A862E /* MachTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899410F57C5600BB2B04 /* MachTask.cpp */; }; + 26D5B0FB11B07550009A862E /* MachThreadContext_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899710F57C5600BB2B04 /* MachThreadContext_arm.cpp */; }; + 26D5B0FC11B07550009A862E /* MachThreadContext_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899910F57C5600BB2B04 /* MachThreadContext_i386.cpp */; }; + 26D5B0FD11B07550009A862E /* MachThreadContext_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899B10F57C5600BB2B04 /* MachThreadContext_x86_64.cpp */; }; + 26D5B0FE11B07550009A862E /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899D10F57C5600BB2B04 /* MachVMMemory.cpp */; }; + 26D5B0FF11B07550009A862E /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C899F10F57C5600BB2B04 /* MachVMRegion.cpp */; }; + 26D5B10011B07550009A862E /* ProcessControl-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 260C89A110F57C5600BB2B04 /* ProcessControl-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 26D5B10111B07550009A862E /* ProcessMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89A310F57C5600BB2B04 /* ProcessMacOSX.cpp */; }; + 26D5B10211B07550009A862E /* ProcessMacOSXLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89A510F57C5600BB2B04 /* ProcessMacOSXLog.cpp */; }; + 26D5B10311B07550009A862E /* RegisterContextMach_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89A910F57C5600BB2B04 /* RegisterContextMach_arm.cpp */; }; + 26D5B10411B07550009A862E /* RegisterContextMach_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89AB10F57C5600BB2B04 /* RegisterContextMach_i386.cpp */; }; + 26D5B10511B07550009A862E /* RegisterContextMach_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89AD10F57C5600BB2B04 /* RegisterContextMach_x86_64.cpp */; }; + 26D5B10611B07550009A862E /* ThreadMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89AF10F57C5600BB2B04 /* ThreadMacOSX.cpp */; }; + 26D5B10711B07550009A862E /* DWARFAbbreviationDeclaration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89B310F57C5600BB2B04 /* DWARFAbbreviationDeclaration.cpp */; }; + 26D5B10811B07550009A862E /* DWARFCompileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89B710F57C5600BB2B04 /* DWARFCompileUnit.cpp */; }; + 26D5B10911B07550009A862E /* DWARFDebugAbbrev.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89B910F57C5600BB2B04 /* DWARFDebugAbbrev.cpp */; }; + 26D5B10A11B07550009A862E /* DWARFDebugAranges.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89BB10F57C5600BB2B04 /* DWARFDebugAranges.cpp */; }; + 26D5B10B11B07550009A862E /* DWARFDebugArangeSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89BD10F57C5600BB2B04 /* DWARFDebugArangeSet.cpp */; }; + 26D5B10C11B07550009A862E /* DWARFDebugInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89BF10F57C5600BB2B04 /* DWARFDebugInfo.cpp */; }; + 26D5B10D11B07550009A862E /* DWARFDebugInfoEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C110F57C5600BB2B04 /* DWARFDebugInfoEntry.cpp */; }; + 26D5B10E11B07550009A862E /* DWARFDebugLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C310F57C5600BB2B04 /* DWARFDebugLine.cpp */; }; + 26D5B10F11B07550009A862E /* DWARFDebugMacinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C510F57C5600BB2B04 /* DWARFDebugMacinfo.cpp */; }; + 26D5B11011B07550009A862E /* DWARFDebugMacinfoEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C710F57C5600BB2B04 /* DWARFDebugMacinfoEntry.cpp */; }; + 26D5B11111B07550009A862E /* DWARFDebugPubnames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89C910F57C5600BB2B04 /* DWARFDebugPubnames.cpp */; }; + 26D5B11211B07550009A862E /* DWARFDebugPubnamesSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89CB10F57C5600BB2B04 /* DWARFDebugPubnamesSet.cpp */; }; + 26D5B11311B07550009A862E /* DWARFDebugRanges.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89CD10F57C5600BB2B04 /* DWARFDebugRanges.cpp */; }; + 26D5B11411B07550009A862E /* DWARFDefines.c in Sources */ = {isa = PBXBuildFile; fileRef = 260C89CF10F57C5600BB2B04 /* DWARFDefines.c */; }; + 26D5B11511B07550009A862E /* DWARFDIECollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D110F57C5600BB2B04 /* DWARFDIECollection.cpp */; }; + 26D5B11611B07550009A862E /* DWARFFormValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D310F57C5600BB2B04 /* DWARFFormValue.cpp */; }; + 26D5B11711B07550009A862E /* DWARFLocationDescription.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D510F57C5600BB2B04 /* DWARFLocationDescription.cpp */; }; + 26D5B11811B07550009A862E /* DWARFLocationList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D710F57C5600BB2B04 /* DWARFLocationList.cpp */; }; + 26D5B11911B07550009A862E /* SymbolFileDWARF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89D910F57C5600BB2B04 /* SymbolFileDWARF.cpp */; }; + 26D5B11A11B07550009A862E /* SymbolFileDWARFDebugMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89DB10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.cpp */; }; + 26D5B11B11B07550009A862E /* SymbolFileSymtab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89DE10F57C5600BB2B04 /* SymbolFileSymtab.cpp */; }; + 26D5B11C11B07550009A862E /* SymbolVendorMacOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C89E210F57C5600BB2B04 /* SymbolVendorMacOSX.cpp */; }; + 26D5B11D11B07550009A862E /* CommandObjectUnalias.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A8B4EA310FD516400C68FF2 /* CommandObjectUnalias.cpp */; }; + 26D5B11E11B07550009A862E /* ScriptInterpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A82010B10FFB49800182560 /* ScriptInterpreter.cpp */; }; + 26D5B11F11B07550009A862E /* BreakpointResolverAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D0DD5310FE555900271C65 /* BreakpointResolverAddress.cpp */; }; + 26D5B12011B07550009A862E /* BreakpointResolverFileLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D0DD5410FE555900271C65 /* BreakpointResolverFileLine.cpp */; }; + 26D5B12111B07550009A862E /* BreakpointResolverName.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D0DD5510FE555900271C65 /* BreakpointResolverName.cpp */; }; + 26D5B12211B07550009A862E /* DisassemblerLLVM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260C897410F57C5600BB2B04 /* DisassemblerLLVM.cpp */; }; + 26D5B12311B07550009A862E /* ThreadPlanRunToAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CAFCE031101218900CA63DB /* ThreadPlanRunToAddress.cpp */; }; + 26D5B12411B07550009A862E /* ThreadPlanShouldStopHere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43DEFA110641F300E55CBF /* ThreadPlanShouldStopHere.cpp */; }; + 26D5B12511B07550009A862E /* ThreadPlanStepInRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43DF8911069C3200E55CBF /* ThreadPlanStepInRange.cpp */; }; + 26D5B12611B07550009A862E /* ThreadPlanStepOverRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43DF8A11069C3200E55CBF /* ThreadPlanStepOverRange.cpp */; }; + 26D5B12711B07550009A862E /* CommandObjectLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264AD83711095BA600E0B039 /* CommandObjectLog.cpp */; }; + 26D5B12811B07550009A862E /* ValueObjectRegister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264334381110F63100CDB6C6 /* ValueObjectRegister.cpp */; }; + 26D5B12911B07550009A862E /* TimeValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B4E28B112F5DCD00AB3F64 /* TimeValue.cpp */; }; + 26D5B12A11B07550009A862E /* UUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C81CA511335651004BDC5A /* UUID.cpp */; }; + 26D5B12B11B07550009A862E /* ScriptInterpreterNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A2771FC1135A37500E6ADB6 /* ScriptInterpreterNone.cpp */; }; + 26D5B12C11B07550009A862E /* CommandObjectCrossref.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DFBC57113B48F300DD817F /* CommandObjectCrossref.cpp */; }; + 26D5B12D11B07550009A862E /* CommandObjectMultiword.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DFBC58113B48F300DD817F /* CommandObjectMultiword.cpp */; }; + 26D5B12E11B07550009A862E /* CommandObjectRegexCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DFBC59113B48F300DD817F /* CommandObjectRegexCommand.cpp */; }; + 26D5B12F11B07550009A862E /* Symbols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2689B0B5113EE47E00A4AEDB /* Symbols.cpp */; }; + 26D5B13011B07550009A862E /* Debugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 263664921140A4930075843B /* Debugger.cpp */; }; + 26D5B13111B07550009A862E /* ProcessGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE62FA1145F2130064CF93 /* ProcessGDBRemote.cpp */; }; + 26D5B13211B07550009A862E /* ProcessGDBRemoteLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE62FC1145F2130064CF93 /* ProcessGDBRemoteLog.cpp */; }; + 26D5B13311B07550009A862E /* ThreadGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE62FE1145F2130064CF93 /* ThreadGDBRemote.cpp */; }; + 26D5B13411B07550009A862E /* GDBRemoteCommunication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FE25221146CADE00F4085A /* GDBRemoteCommunication.cpp */; }; + 26D5B13511B07550009A862E /* GDBRemoteRegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 261E18CD1148966100BADCD3 /* GDBRemoteRegisterContext.cpp */; }; + 26D5B13611B07550009A862E /* UnixSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C00987011500B4300F316B0 /* UnixSignals.cpp */; }; + 26D5B13711B07550009A862E /* LogChannelDWARF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26109B3B1155D70100CC3529 /* LogChannelDWARF.cpp */; }; + 26D5B13811B07550009A862E /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */; }; + 26D5B13911B07550009A862E /* LLDBWrapPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */; }; + 26D5B13A11B07550009A862E /* StringList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A35765F116E76B900E8ED2F /* StringList.cpp */; }; + 26D5B13B11B07550009A862E /* CommandCompletions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CB74116BD98B00C7A725 /* CommandCompletions.cpp */; }; + 26D5B13C11B07550009A862E /* AddressResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC7034011752C6B0086C050 /* AddressResolver.cpp */; }; + 26D5B13D11B07550009A862E /* AddressResolverFileLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC7034211752C720086C050 /* AddressResolverFileLine.cpp */; }; + 26D5B13E11B07550009A862E /* AddressResolverName.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC7034411752C790086C050 /* AddressResolverName.cpp */; }; + 26D5B13F11B07550009A862E /* ObjectContainerBSDArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */; }; + 26D5B14011B07550009A862E /* CommandObjectBreakpointCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A42976211861AA600FE05CD /* CommandObjectBreakpointCommand.cpp */; }; + 26D5B14111B07550009A862E /* InputReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AA69DB5118A027A00D753A0 /* InputReader.cpp */; }; + 26D5B14211B07550009A862E /* CommandObjectFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672D8461189055500FF4019 /* CommandObjectFrame.cpp */; }; + 26D5B14311B07550009A862E /* ABISysV_x86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 493C63F11189203300914D5E /* ABISysV_x86_64.cpp */; }; + 26D5B14411B07550009A862E /* ABI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 497E7B9D1188F6690065CCA1 /* ABI.cpp */; }; + 26D5B14511B07550009A862E /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9F611922A1300958FBD /* StringExtractor.cpp */; }; + 26D5B14611B07550009A862E /* ThreadPlanStepUntil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9FE11922A7F00958FBD /* ThreadPlanStepUntil.cpp */; }; + 26D5B14711B07550009A862E /* ThreadPlanCallFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49EC3E98118F90AC00B1265E /* ThreadPlanCallFunction.cpp */; }; + 26D5B14811B07550009A862E /* ClangFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C98D3DA118FB96F00E575D0 /* ClangFunction.cpp */; }; + 26D5B14911B07550009A862E /* RecordingMemoryManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C98D3DB118FB96F00E575D0 /* RecordingMemoryManager.cpp */; settings = {COMPILER_FLAGS = "-fno-rtti"; }; }; + 26D5B14A11B07550009A862E /* CommandObjectCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C98D3E4118FB9B100E575D0 /* CommandObjectCall.cpp */; }; + 26D5B14B11B07550009A862E /* CommandObjectTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 269416AD119A024800FF2715 /* CommandObjectTarget.cpp */; }; + 26D5B14C11B07550009A862E /* PathMappingList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 495BBACB119A0DBE00418BEA /* PathMappingList.cpp */; }; + 26D5B14D11B07550009A862E /* StringExtractorGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */; }; + 26D5B14E11B07550009A862E /* MacOSXLibunwindCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9654F79C1197DA1300F72B43 /* MacOSXLibunwindCallbacks.cpp */; }; + 26D5B15011B07550009A862E /* Registers.s in Sources */ = {isa = PBXBuildFile; fileRef = 9654F7B51197DA3F00F72B43 /* Registers.s */; }; + 26D5B15111B07550009A862E /* unw_getcontext.s in Sources */ = {isa = PBXBuildFile; fileRef = 9654F7BA1197DA3F00F72B43 /* unw_getcontext.s */; }; + 26D5B15211B07550009A862E /* LibUnwindRegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B4666F11A2091600CF6220 /* LibUnwindRegisterContext.cpp */; }; + 26D5B15311B07550009A862E /* ABIMacOSX_i386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 497650CE11A21BEE008DDB57 /* ABIMacOSX_i386.cpp */; }; + 26D5B15411B07550009A862E /* ObjCTrampolineHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C51FF1611A4C486007C962F /* ObjCTrampolineHandler.cpp */; }; + 26D5B15511B07550009A862E /* Baton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A0604811A5D03C00F75969 /* Baton.cpp */; }; + 26D5B15611B07550009A862E /* CommandObjectArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 499F381F11A5B3F300F5CE02 /* CommandObjectArgs.cpp */; }; + 26D5B15711B07550009A862E /* ThreadPlanStepThroughObjCTrampoline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4473E11A8687100EF971E /* ThreadPlanStepThroughObjCTrampoline.cpp */; }; + 26D5B15911B07550009A862E /* UnwindLibUnwind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E3EEBE11A98A1900FBADB6 /* UnwindLibUnwind.cpp */; }; + 26D5B15A11B07550009A862E /* UnwindMacOSXFrameBackchain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E3EEE311A9901300FBADB6 /* UnwindMacOSXFrameBackchain.cpp */; }; + 26D5B15B11B07550009A862E /* RegisterContextMacOSXFrameBackchain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E3EEF711A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.cpp */; }; + 26D5B15C11B07550009A862E /* ObjCObjectPrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49BF48DC11ADF356008863BD /* ObjCObjectPrinter.cpp */; }; + 26D5B18E11B07979009A862E /* libuwind.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 9654F7B31197DA3F00F72B43 /* libuwind.cxx */; }; + 26DE1E6B11616C2E00A093E2 /* lldb-forward-rtti.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE1E6911616C2E00A093E2 /* lldb-forward-rtti.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE1E6C11616C2E00A093E2 /* lldb-forward.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE1E6A11616C2E00A093E2 /* lldb-forward.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE204111618AB900A093E2 /* SBSymbolContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE204011618AB900A093E2 /* SBSymbolContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE204311618ACA00A093E2 /* SBAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE204211618ACA00A093E2 /* SBAddress.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE204511618ADA00A093E2 /* SBAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE204411618ADA00A093E2 /* SBAddress.cpp */; }; + 26DE204711618AED00A093E2 /* SBSymbolContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE204611618AED00A093E2 /* SBSymbolContext.cpp */; }; + 26DE204D11618E7A00A093E2 /* SBModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE204C11618E7A00A093E2 /* SBModule.cpp */; }; + 26DE204F11618E9800A093E2 /* SBModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE204E11618E9800A093E2 /* SBModule.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE205311618FAC00A093E2 /* SBFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE205211618FAC00A093E2 /* SBFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE205511618FB800A093E2 /* SBCompileUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE205411618FB800A093E2 /* SBCompileUnit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE205711618FC500A093E2 /* SBBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE205611618FC500A093E2 /* SBBlock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE205911618FE700A093E2 /* SBLineEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE205811618FE700A093E2 /* SBLineEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE205B11618FF600A093E2 /* SBSymbol.h in Headers */ = {isa = PBXBuildFile; fileRef = 26DE205A11618FF600A093E2 /* SBSymbol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 26DE205D1161901400A093E2 /* SBFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE205C1161901400A093E2 /* SBFunction.cpp */; }; + 26DE205F1161901B00A093E2 /* SBCompileUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE205E1161901B00A093E2 /* SBCompileUnit.cpp */; }; + 26DE20611161902700A093E2 /* SBBlock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE20601161902600A093E2 /* SBBlock.cpp */; }; + 26DE20631161904200A093E2 /* SBLineEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE20621161904200A093E2 /* SBLineEntry.cpp */; }; + 26DE20651161904E00A093E2 /* SBSymbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26DE20641161904E00A093E2 /* SBSymbol.cpp */; }; + 26F5C27710F3D9E4009D5894 /* Driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F5C27310F3D9E4009D5894 /* Driver.cpp */; }; + 26F5C27810F3D9E4009D5894 /* IOChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F5C27510F3D9E4009D5894 /* IOChannel.cpp */; }; + 26F5C32510F3DF23009D5894 /* libpython2.6.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32410F3DF23009D5894 /* libpython2.6.dylib */; }; + 26F5C32C10F3DFDD009D5894 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32A10F3DFDD009D5894 /* libedit.dylib */; }; + 26F5C32D10F3DFDD009D5894 /* libtermcap.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C32B10F3DFDD009D5894 /* libtermcap.dylib */; }; + 26F5C37510F3F61B009D5894 /* libobjc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C37410F3F61B009D5894 /* libobjc.dylib */; }; + 26F5C39110F3FA26009D5894 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26F5C39010F3FA26009D5894 /* CoreFoundation.framework */; }; + 49D7072711B5AD03001AD875 /* ClangASTSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 49D7072611B5AD03001AD875 /* ClangASTSource.h */; }; + 49D7072911B5AD11001AD875 /* ClangASTSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49D7072811B5AD11001AD875 /* ClangASTSource.cpp */; settings = {COMPILER_FLAGS = "-fno-rtti"; }; }; + 49F1A74611B3388F003ED505 /* ClangExpressionDeclMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49F1A74511B3388F003ED505 /* ClangExpressionDeclMap.cpp */; }; + 49F1A74A11B338AE003ED505 /* ClangExpressionDeclMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 49F1A74911B338AE003ED505 /* ClangExpressionDeclMap.h */; }; + 4CA9637B11B6E99A00780E28 /* CommandObjectApropos.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9637911B6E99A00780E28 /* CommandObjectApropos.cpp */; }; + 4CA9637C11B6E99A00780E28 /* CommandObjectApropos.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CA9637A11B6E99A00780E28 /* CommandObjectApropos.h */; }; + 9A19A6AF1163BBB200E0D453 /* SBValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A19A6A51163BB7E00E0D453 /* SBValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A19A6B01163BBB300E0D453 /* SBValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A19A6AD1163BB9800E0D453 /* SBValue.cpp */; }; + 9A357583116CFDEE00E8ED2F /* SBValueList.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A357582116CFDEE00E8ED2F /* SBValueList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A35758E116CFE0F00E8ED2F /* SBValueList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A35758D116CFE0F00E8ED2F /* SBValueList.cpp */; }; + 9A357671116E7B5200E8ED2F /* SBStringList.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A357670116E7B5200E8ED2F /* SBStringList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A357673116E7B6400E8ED2F /* SBStringList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A357672116E7B6400E8ED2F /* SBStringList.cpp */; }; + 9A3576A8116E9AB700E8ED2F /* SBHostOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3576A7116E9AB700E8ED2F /* SBHostOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A3576AA116E9AC700E8ED2F /* SBHostOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A3576A9116E9AC700E8ED2F /* SBHostOS.cpp */; }; + 9AA69DA61188F52100D753A0 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */; }; + 9AA69DAF118A023300D753A0 /* SBInputReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AA69DAE118A023300D753A0 /* SBInputReader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9AA69DB1118A024600D753A0 /* SBInputReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AA69DB0118A024600D753A0 /* SBInputReader.cpp */; }; + 9AC7038E117674FB0086C050 /* SBInstruction.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AC7038D117674EB0086C050 /* SBInstruction.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9AC70390117675270086C050 /* SBInstructionList.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AC7038F117675270086C050 /* SBInstructionList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9AC703AF117675410086C050 /* SBInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703AE117675410086C050 /* SBInstruction.cpp */; }; + 9AC703B1117675490086C050 /* SBInstructionList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703B0117675490086C050 /* SBInstructionList.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 262CFC7111A450CB00946C6C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 26CE0593115C31C20022F371; + remoteInfo = debugserver; + }; + 266803611160110D008E1FE4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26680206115FD0ED008E1FE4; + remoteInfo = LLDB; + }; + 26CE059F115C31E50022F371 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 26CE0594115C31C20022F371; + remoteInfo = "lldb-debugserver"; + }; + 26CE060F115C438C0022F371 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 26CE0593115C31C20022F371; + remoteInfo = "lldb-debugserver"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 260223E7115F06D500A601A2 /* SBCommunication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBCommunication.h; path = include/lldb/API/SBCommunication.h; sourceTree = ""; }; + 260223E8115F06E500A601A2 /* SBCommunication.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBCommunication.cpp; path = source/API/SBCommunication.cpp; sourceTree = ""; }; + 26022531115F27FA00A601A2 /* SBFileSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBFileSpec.h; path = include/lldb/API/SBFileSpec.h; sourceTree = ""; }; + 26022532115F281400A601A2 /* SBFileSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBFileSpec.cpp; path = source/API/SBFileSpec.cpp; sourceTree = ""; }; + 260C847010F50EFC00BB2B04 /* ThreadPlanContinue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanContinue.cpp; path = source/Target/ThreadPlanContinue.cpp; sourceTree = ""; }; + 260C847110F50EFC00BB2B04 /* ThreadPlanBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanBase.cpp; path = source/Target/ThreadPlanBase.cpp; sourceTree = ""; }; + 260C847210F50EFC00BB2B04 /* ThreadPlanStepInstruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepInstruction.cpp; path = source/Target/ThreadPlanStepInstruction.cpp; sourceTree = ""; }; + 260C847310F50EFC00BB2B04 /* ThreadPlanStepOut.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepOut.cpp; path = source/Target/ThreadPlanStepOut.cpp; sourceTree = ""; }; + 260C847410F50EFC00BB2B04 /* ThreadPlanStepOverBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepOverBreakpoint.cpp; path = source/Target/ThreadPlanStepOverBreakpoint.cpp; sourceTree = ""; }; + 260C847510F50EFC00BB2B04 /* ThreadPlanStepThrough.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepThrough.cpp; path = source/Target/ThreadPlanStepThrough.cpp; sourceTree = ""; }; + 260C847610F50EFC00BB2B04 /* ThreadPlanStepRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepRange.cpp; path = source/Target/ThreadPlanStepRange.cpp; sourceTree = ""; }; + 260C847E10F50F0A00BB2B04 /* ThreadPlanContinue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanContinue.h; path = include/lldb/Target/ThreadPlanContinue.h; sourceTree = ""; }; + 260C847F10F50F0A00BB2B04 /* ThreadPlanBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanBase.h; path = include/lldb/Target/ThreadPlanBase.h; sourceTree = ""; }; + 260C848010F50F0A00BB2B04 /* ThreadPlanStepInstruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepInstruction.h; path = include/lldb/Target/ThreadPlanStepInstruction.h; sourceTree = ""; }; + 260C848110F50F0A00BB2B04 /* ThreadPlanStepOut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepOut.h; path = include/lldb/Target/ThreadPlanStepOut.h; sourceTree = ""; }; + 260C848210F50F0A00BB2B04 /* ThreadPlanStepOverBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepOverBreakpoint.h; path = include/lldb/Target/ThreadPlanStepOverBreakpoint.h; sourceTree = ""; }; + 260C848310F50F0A00BB2B04 /* ThreadPlanStepThrough.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepThrough.h; path = include/lldb/Target/ThreadPlanStepThrough.h; sourceTree = ""; }; + 260C848410F50F0A00BB2B04 /* ThreadPlanStepRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepRange.h; path = include/lldb/Target/ThreadPlanStepRange.h; sourceTree = ""; }; + 260C876910F538E700BB2B04 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 260C897410F57C5600BB2B04 /* DisassemblerLLVM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DisassemblerLLVM.cpp; sourceTree = ""; }; + 260C897510F57C5600BB2B04 /* DisassemblerLLVM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisassemblerLLVM.h; sourceTree = ""; }; + 260C897A10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderMacOSXDYLD.cpp; sourceTree = ""; }; + 260C897B10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderMacOSXDYLD.h; sourceTree = ""; }; + 260C897C10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLDLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderMacOSXDYLDLog.cpp; sourceTree = ""; }; + 260C897D10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLDLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderMacOSXDYLDLog.h; sourceTree = ""; }; + 260C898010F57C5600BB2B04 /* ObjectContainerUniversalMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectContainerUniversalMachO.cpp; sourceTree = ""; }; + 260C898110F57C5600BB2B04 /* ObjectContainerUniversalMachO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectContainerUniversalMachO.h; sourceTree = ""; }; + 260C898410F57C5600BB2B04 /* elf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = elf.h; sourceTree = ""; }; + 260C898510F57C5600BB2B04 /* ObjectFileELF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectFileELF.cpp; sourceTree = ""; }; + 260C898610F57C5600BB2B04 /* ObjectFileELF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectFileELF.h; sourceTree = ""; }; + 260C898810F57C5600BB2B04 /* ObjectFileMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectFileMachO.cpp; sourceTree = ""; }; + 260C898910F57C5600BB2B04 /* ObjectFileMachO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectFileMachO.h; sourceTree = ""; }; + 260C898D10F57C5600BB2B04 /* cc-swig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "cc-swig"; sourceTree = ""; }; + 260C898E10F57C5600BB2B04 /* config.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = config.pl; sourceTree = ""; }; + 260C898F10F57C5600BB2B04 /* test-ProcessDebug.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "test-ProcessDebug.pl"; sourceTree = ""; }; + 260C899210F57C5600BB2B04 /* MachException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachException.cpp; sourceTree = ""; }; + 260C899310F57C5600BB2B04 /* MachException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachException.h; sourceTree = ""; }; + 260C899410F57C5600BB2B04 /* MachTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachTask.cpp; sourceTree = ""; }; + 260C899510F57C5600BB2B04 /* MachTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachTask.h; sourceTree = ""; }; + 260C899610F57C5600BB2B04 /* MachThreadContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachThreadContext.h; sourceTree = ""; }; + 260C899710F57C5600BB2B04 /* MachThreadContext_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachThreadContext_arm.cpp; sourceTree = ""; }; + 260C899810F57C5600BB2B04 /* MachThreadContext_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachThreadContext_arm.h; sourceTree = ""; }; + 260C899910F57C5600BB2B04 /* MachThreadContext_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachThreadContext_i386.cpp; sourceTree = ""; }; + 260C899A10F57C5600BB2B04 /* MachThreadContext_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachThreadContext_i386.h; sourceTree = ""; }; + 260C899B10F57C5600BB2B04 /* MachThreadContext_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachThreadContext_x86_64.cpp; sourceTree = ""; }; + 260C899C10F57C5600BB2B04 /* MachThreadContext_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachThreadContext_x86_64.h; sourceTree = ""; }; + 260C899D10F57C5600BB2B04 /* MachVMMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMMemory.cpp; sourceTree = ""; }; + 260C899E10F57C5600BB2B04 /* MachVMMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachVMMemory.h; sourceTree = ""; }; + 260C899F10F57C5600BB2B04 /* MachVMRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMRegion.cpp; sourceTree = ""; }; + 260C89A010F57C5600BB2B04 /* MachVMRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachVMRegion.h; sourceTree = ""; }; + 260C89A110F57C5600BB2B04 /* ProcessControl-mig.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = "ProcessControl-mig.defs"; sourceTree = ""; }; + 260C89A310F57C5600BB2B04 /* ProcessMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMacOSX.cpp; sourceTree = ""; }; + 260C89A410F57C5600BB2B04 /* ProcessMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessMacOSX.h; sourceTree = ""; }; + 260C89A510F57C5600BB2B04 /* ProcessMacOSXLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMacOSXLog.cpp; sourceTree = ""; }; + 260C89A610F57C5600BB2B04 /* ProcessMacOSXLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessMacOSXLog.h; sourceTree = ""; }; + 260C89A910F57C5600BB2B04 /* RegisterContextMach_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextMach_arm.cpp; sourceTree = ""; }; + 260C89AA10F57C5600BB2B04 /* RegisterContextMach_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextMach_arm.h; sourceTree = ""; }; + 260C89AB10F57C5600BB2B04 /* RegisterContextMach_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextMach_i386.cpp; sourceTree = ""; }; + 260C89AC10F57C5600BB2B04 /* RegisterContextMach_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextMach_i386.h; sourceTree = ""; }; + 260C89AD10F57C5600BB2B04 /* RegisterContextMach_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterContextMach_x86_64.cpp; sourceTree = ""; }; + 260C89AE10F57C5600BB2B04 /* RegisterContextMach_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterContextMach_x86_64.h; sourceTree = ""; }; + 260C89AF10F57C5600BB2B04 /* ThreadMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadMacOSX.cpp; sourceTree = ""; }; + 260C89B010F57C5600BB2B04 /* ThreadMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadMacOSX.h; sourceTree = ""; }; + 260C89B310F57C5600BB2B04 /* DWARFAbbreviationDeclaration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFAbbreviationDeclaration.cpp; sourceTree = ""; }; + 260C89B410F57C5600BB2B04 /* DWARFAbbreviationDeclaration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFAbbreviationDeclaration.h; sourceTree = ""; }; + 260C89B610F57C5600BB2B04 /* DWARFAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFAttribute.h; sourceTree = ""; }; + 260C89B710F57C5600BB2B04 /* DWARFCompileUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFCompileUnit.cpp; sourceTree = ""; }; + 260C89B810F57C5600BB2B04 /* DWARFCompileUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFCompileUnit.h; sourceTree = ""; }; + 260C89B910F57C5600BB2B04 /* DWARFDebugAbbrev.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugAbbrev.cpp; sourceTree = ""; }; + 260C89BA10F57C5600BB2B04 /* DWARFDebugAbbrev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugAbbrev.h; sourceTree = ""; }; + 260C89BB10F57C5600BB2B04 /* DWARFDebugAranges.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugAranges.cpp; sourceTree = ""; }; + 260C89BC10F57C5600BB2B04 /* DWARFDebugAranges.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugAranges.h; sourceTree = ""; }; + 260C89BD10F57C5600BB2B04 /* DWARFDebugArangeSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugArangeSet.cpp; sourceTree = ""; }; + 260C89BE10F57C5600BB2B04 /* DWARFDebugArangeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugArangeSet.h; sourceTree = ""; }; + 260C89BF10F57C5600BB2B04 /* DWARFDebugInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugInfo.cpp; sourceTree = ""; }; + 260C89C010F57C5600BB2B04 /* DWARFDebugInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugInfo.h; sourceTree = ""; }; + 260C89C110F57C5600BB2B04 /* DWARFDebugInfoEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugInfoEntry.cpp; sourceTree = ""; }; + 260C89C210F57C5600BB2B04 /* DWARFDebugInfoEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugInfoEntry.h; sourceTree = ""; }; + 260C89C310F57C5600BB2B04 /* DWARFDebugLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugLine.cpp; sourceTree = ""; }; + 260C89C410F57C5600BB2B04 /* DWARFDebugLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugLine.h; sourceTree = ""; }; + 260C89C510F57C5600BB2B04 /* DWARFDebugMacinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugMacinfo.cpp; sourceTree = ""; }; + 260C89C610F57C5600BB2B04 /* DWARFDebugMacinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugMacinfo.h; sourceTree = ""; }; + 260C89C710F57C5600BB2B04 /* DWARFDebugMacinfoEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugMacinfoEntry.cpp; sourceTree = ""; }; + 260C89C810F57C5600BB2B04 /* DWARFDebugMacinfoEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugMacinfoEntry.h; sourceTree = ""; }; + 260C89C910F57C5600BB2B04 /* DWARFDebugPubnames.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugPubnames.cpp; sourceTree = ""; }; + 260C89CA10F57C5600BB2B04 /* DWARFDebugPubnames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugPubnames.h; sourceTree = ""; }; + 260C89CB10F57C5600BB2B04 /* DWARFDebugPubnamesSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugPubnamesSet.cpp; sourceTree = ""; }; + 260C89CC10F57C5600BB2B04 /* DWARFDebugPubnamesSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugPubnamesSet.h; sourceTree = ""; }; + 260C89CD10F57C5600BB2B04 /* DWARFDebugRanges.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDebugRanges.cpp; sourceTree = ""; }; + 260C89CE10F57C5600BB2B04 /* DWARFDebugRanges.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDebugRanges.h; sourceTree = ""; }; + 260C89CF10F57C5600BB2B04 /* DWARFDefines.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DWARFDefines.c; sourceTree = ""; }; + 260C89D010F57C5600BB2B04 /* DWARFDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDefines.h; sourceTree = ""; }; + 260C89D110F57C5600BB2B04 /* DWARFDIECollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFDIECollection.cpp; sourceTree = ""; }; + 260C89D210F57C5600BB2B04 /* DWARFDIECollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFDIECollection.h; sourceTree = ""; }; + 260C89D310F57C5600BB2B04 /* DWARFFormValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFFormValue.cpp; sourceTree = ""; }; + 260C89D410F57C5600BB2B04 /* DWARFFormValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFFormValue.h; sourceTree = ""; }; + 260C89D510F57C5600BB2B04 /* DWARFLocationDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFLocationDescription.cpp; sourceTree = ""; }; + 260C89D610F57C5600BB2B04 /* DWARFLocationDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFLocationDescription.h; sourceTree = ""; }; + 260C89D710F57C5600BB2B04 /* DWARFLocationList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFLocationList.cpp; sourceTree = ""; }; + 260C89D810F57C5600BB2B04 /* DWARFLocationList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFLocationList.h; sourceTree = ""; }; + 260C89D910F57C5600BB2B04 /* SymbolFileDWARF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolFileDWARF.cpp; sourceTree = ""; }; + 260C89DA10F57C5600BB2B04 /* SymbolFileDWARF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolFileDWARF.h; sourceTree = ""; }; + 260C89DB10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolFileDWARFDebugMap.cpp; sourceTree = ""; }; + 260C89DC10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolFileDWARFDebugMap.h; sourceTree = ""; }; + 260C89DE10F57C5600BB2B04 /* SymbolFileSymtab.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolFileSymtab.cpp; sourceTree = ""; }; + 260C89DF10F57C5600BB2B04 /* SymbolFileSymtab.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolFileSymtab.h; sourceTree = ""; }; + 260C89E210F57C5600BB2B04 /* SymbolVendorMacOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolVendorMacOSX.cpp; sourceTree = ""; }; + 260C89E310F57C5600BB2B04 /* SymbolVendorMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolVendorMacOSX.h; sourceTree = ""; }; + 26109B3B1155D70100CC3529 /* LogChannelDWARF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogChannelDWARF.cpp; sourceTree = ""; }; + 26109B3C1155D70100CC3529 /* LogChannelDWARF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogChannelDWARF.h; sourceTree = ""; }; + 261744771168585B005ADD65 /* SBType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBType.cpp; path = source/API/SBType.cpp; sourceTree = ""; }; + 2617447911685869005ADD65 /* SBType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBType.h; path = include/lldb/API/SBType.h; sourceTree = ""; }; + 261E18CC1148966100BADCD3 /* GDBRemoteRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GDBRemoteRegisterContext.h; path = "source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h"; sourceTree = ""; }; + 261E18CD1148966100BADCD3 /* GDBRemoteRegisterContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GDBRemoteRegisterContext.cpp; path = "source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp"; sourceTree = ""; }; + 263664921140A4930075843B /* Debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Debugger.cpp; path = source/Core/Debugger.cpp; sourceTree = ""; }; + 263664941140A4C10075843B /* Debugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Debugger.h; path = include/lldb/Core/Debugger.h; sourceTree = ""; }; + 263FEDA5112CC1DA00E4C208 /* ThreadSafeSTLMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadSafeSTLMap.h; path = include/lldb/Core/ThreadSafeSTLMap.h; sourceTree = ""; }; + 264334381110F63100CDB6C6 /* ValueObjectRegister.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectRegister.cpp; path = source/Core/ValueObjectRegister.cpp; sourceTree = ""; }; + 2643343A1110F63C00CDB6C6 /* ValueObjectRegister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectRegister.h; path = include/lldb/Core/ValueObjectRegister.h; sourceTree = ""; }; + 264AD83711095BA600E0B039 /* CommandObjectLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectLog.cpp; path = source/Commands/CommandObjectLog.cpp; sourceTree = ""; }; + 264AD83911095BBD00E0B039 /* CommandObjectLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectLog.h; path = source/Commands/CommandObjectLog.h; sourceTree = ""; }; + 265ABF6210F42EE900531910 /* DebugSymbols.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DebugSymbols.framework; path = /System/Library/PrivateFrameworks/DebugSymbols.framework; sourceTree = ""; }; + 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = debugserver.xcodeproj; path = tools/debugserver/debugserver.xcodeproj; sourceTree = ""; }; + 2660D9F611922A1300958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = source/Utility/StringExtractor.cpp; sourceTree = ""; }; + 2660D9F711922A1300958FBD /* StringExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = source/Utility/StringExtractor.h; sourceTree = ""; }; + 2660D9FE11922A7F00958FBD /* ThreadPlanStepUntil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepUntil.cpp; path = source/Target/ThreadPlanStepUntil.cpp; sourceTree = ""; }; + 26680207115FD0ED008E1FE4 /* LLDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LLDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 266960591199F4230075C61A /* build-llvm.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "build-llvm.pl"; sourceTree = ""; }; + 2669605A1199F4230075C61A /* build-swig-wrapper-classes.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "build-swig-wrapper-classes.sh"; sourceTree = ""; }; + 2669605B1199F4230075C61A /* checkpoint-llvm.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "checkpoint-llvm.pl"; sourceTree = ""; }; + 2669605C1199F4230075C61A /* finish-swig-wrapper-classes.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "finish-swig-wrapper-classes.sh"; sourceTree = ""; }; + 2669605D1199F4230075C61A /* install-lldb.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "install-lldb.sh"; sourceTree = ""; }; + 2669605E1199F4230075C61A /* lldb.swig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = lldb.swig; sourceTree = ""; }; + 266960601199F4230075C61A /* build-swig-Python.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "build-swig-Python.sh"; sourceTree = ""; }; + 266960611199F4230075C61A /* edit-swig-python-wrapper-file.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = "edit-swig-python-wrapper-file.py"; sourceTree = ""; }; + 266960621199F4230075C61A /* finish-swig-Python-lldb.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "finish-swig-Python-lldb.sh"; sourceTree = ""; }; + 266960631199F4230075C61A /* sed-sources */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "sed-sources"; sourceTree = ""; }; + 2672D8461189055500FF4019 /* CommandObjectFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectFrame.cpp; path = source/Commands/CommandObjectFrame.cpp; sourceTree = ""; }; + 2672D8471189055500FF4019 /* CommandObjectFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectFrame.h; path = source/Commands/CommandObjectFrame.h; sourceTree = ""; }; + 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractorGDBRemote.cpp; path = source/Utility/StringExtractorGDBRemote.cpp; sourceTree = ""; }; + 2676A094119C93C8008A98EF /* StringExtractorGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringExtractorGDBRemote.h; path = source/Utility/StringExtractorGDBRemote.h; sourceTree = ""; }; + 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PseudoTerminal.cpp; path = source/Utility/PseudoTerminal.cpp; sourceTree = ""; }; + 2682F16B115EDA0D00CCFF99 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PseudoTerminal.h; path = source/Utility/PseudoTerminal.h; sourceTree = ""; }; + 2682F284115EF3A700CCFF99 /* SBError.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBError.cpp; path = source/API/SBError.cpp; sourceTree = ""; }; + 2682F286115EF3BD00CCFF99 /* SBError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBError.h; path = include/lldb/API/SBError.h; sourceTree = ""; }; + 2689B0A4113EE3CD00A4AEDB /* Symbols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Symbols.h; path = include/lldb/Host/Symbols.h; sourceTree = ""; }; + 2689B0B5113EE47E00A4AEDB /* Symbols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Symbols.cpp; path = source/Host/macosx/Symbols.cpp; sourceTree = ""; }; + 268A813F115B19D000F645B0 /* UniqueCStringMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UniqueCStringMap.h; path = include/lldb/Core/UniqueCStringMap.h; sourceTree = ""; }; + 269416AD119A024800FF2715 /* CommandObjectTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectTarget.cpp; path = source/Commands/CommandObjectTarget.cpp; sourceTree = ""; }; + 269416AE119A024800FF2715 /* CommandObjectTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectTarget.h; path = source/Commands/CommandObjectTarget.h; sourceTree = ""; }; + 26A0604711A5BC7A00F75969 /* Baton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Baton.h; path = include/lldb/Core/Baton.h; sourceTree = ""; }; + 26A0604811A5D03C00F75969 /* Baton.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Baton.cpp; path = source/Core/Baton.cpp; sourceTree = ""; }; + 26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectContainerBSDArchive.cpp; sourceTree = ""; }; + 26A3B4AD1181454800381BC2 /* ObjectContainerBSDArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectContainerBSDArchive.h; sourceTree = ""; }; + 26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLDBWrapPython.cpp; path = source/LLDBWrapPython.cpp; sourceTree = ""; }; + 26B167A41123BF5500DC7B4F /* ThreadSafeValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadSafeValue.h; path = include/lldb/Core/ThreadSafeValue.h; sourceTree = ""; }; + 26B42B1E1187A92B0079C8C8 /* lldb-include.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-include.h"; path = "include/lldb/lldb-include.h"; sourceTree = ""; }; + 26B42C4C1187ABA50079C8C8 /* LLDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLDB.h; path = include/lldb/API/LLDB.h; sourceTree = ""; }; + 26B4666F11A2091600CF6220 /* LibUnwindRegisterContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibUnwindRegisterContext.cpp; path = Utility/LibUnwindRegisterContext.cpp; sourceTree = ""; }; + 26B4667011A2091600CF6220 /* LibUnwindRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibUnwindRegisterContext.h; path = Utility/LibUnwindRegisterContext.h; sourceTree = ""; }; + 26B4E26E112F35F700AB3F64 /* TimeValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TimeValue.h; path = include/lldb/Host/TimeValue.h; sourceTree = ""; }; + 26B4E28B112F5DCD00AB3F64 /* TimeValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TimeValue.cpp; path = source/Host/macosx/TimeValue.cpp; sourceTree = ""; }; + 26BC7C2510F1B3BC00F91463 /* lldb-defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-defines.h"; path = "include/lldb/lldb-defines.h"; sourceTree = ""; }; + 26BC7C2610F1B3BC00F91463 /* lldb-enumerations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-enumerations.h"; path = "include/lldb/lldb-enumerations.h"; sourceTree = ""; }; + 26BC7C2810F1B3BC00F91463 /* lldb-private-interfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-private-interfaces.h"; path = "include/lldb/lldb-private-interfaces.h"; sourceTree = ""; }; + 26BC7C2910F1B3BC00F91463 /* lldb-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-types.h"; path = "include/lldb/lldb-types.h"; sourceTree = ""; }; + 26BC7C2A10F1B3BC00F91463 /* lldb-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-private.h"; path = "include/lldb/lldb-private.h"; sourceTree = ""; }; + 26BC7C5510F1B6E900F91463 /* Block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Block.h; path = include/lldb/Symbol/Block.h; sourceTree = ""; }; + 26BC7C5610F1B6E900F91463 /* ClangASTContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangASTContext.h; path = include/lldb/Symbol/ClangASTContext.h; sourceTree = ""; }; + 26BC7C5710F1B6E900F91463 /* CompileUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompileUnit.h; path = include/lldb/Symbol/CompileUnit.h; sourceTree = ""; }; + 26BC7C5810F1B6E900F91463 /* Declaration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Declaration.h; path = include/lldb/Symbol/Declaration.h; sourceTree = ""; }; + 26BC7C5910F1B6E900F91463 /* DWARFCallFrameInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DWARFCallFrameInfo.h; path = include/lldb/Symbol/DWARFCallFrameInfo.h; sourceTree = ""; }; + 26BC7C5A10F1B6E900F91463 /* Function.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Function.h; path = include/lldb/Symbol/Function.h; sourceTree = ""; }; + 26BC7C5B10F1B6E900F91463 /* LineEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LineEntry.h; path = include/lldb/Symbol/LineEntry.h; sourceTree = ""; }; + 26BC7C5C10F1B6E900F91463 /* LineTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LineTable.h; path = include/lldb/Symbol/LineTable.h; sourceTree = ""; }; + 26BC7C5D10F1B6E900F91463 /* ObjectContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjectContainer.h; path = include/lldb/Symbol/ObjectContainer.h; sourceTree = ""; }; + 26BC7C5E10F1B6E900F91463 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = include/lldb/Symbol/ObjectFile.h; sourceTree = ""; }; + 26BC7C5F10F1B6E900F91463 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Symbol.h; path = include/lldb/Symbol/Symbol.h; sourceTree = ""; }; + 26BC7C6010F1B6E900F91463 /* SymbolContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolContext.h; path = include/lldb/Symbol/SymbolContext.h; sourceTree = ""; }; + 26BC7C6110F1B6E900F91463 /* SymbolContextScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolContextScope.h; path = include/lldb/Symbol/SymbolContextScope.h; sourceTree = ""; }; + 26BC7C6210F1B6E900F91463 /* SymbolFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolFile.h; path = include/lldb/Symbol/SymbolFile.h; sourceTree = ""; }; + 26BC7C6310F1B6E900F91463 /* SymbolVendor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolVendor.h; path = include/lldb/Symbol/SymbolVendor.h; sourceTree = ""; }; + 26BC7C6410F1B6E900F91463 /* Symtab.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Symtab.h; path = include/lldb/Symbol/Symtab.h; sourceTree = ""; }; + 26BC7C6510F1B6E900F91463 /* Type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Type.h; path = include/lldb/Symbol/Type.h; sourceTree = ""; }; + 26BC7C6610F1B6E900F91463 /* TypeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TypeList.h; path = include/lldb/Symbol/TypeList.h; sourceTree = ""; }; + 26BC7C6710F1B6E900F91463 /* Variable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Variable.h; path = include/lldb/Symbol/Variable.h; sourceTree = ""; }; + 26BC7C6810F1B6E900F91463 /* VariableList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VariableList.h; path = include/lldb/Symbol/VariableList.h; sourceTree = ""; }; + 26BC7CED10F1B71400F91463 /* StoppointCallbackContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StoppointCallbackContext.h; path = include/lldb/Breakpoint/StoppointCallbackContext.h; sourceTree = ""; }; + 26BC7CEE10F1B71400F91463 /* Breakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpoint.h; path = include/lldb/Breakpoint/Breakpoint.h; sourceTree = ""; }; + 26BC7CEF10F1B71400F91463 /* BreakpointID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointID.h; path = include/lldb/Breakpoint/BreakpointID.h; sourceTree = ""; }; + 26BC7CF010F1B71400F91463 /* BreakpointIDList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointIDList.h; path = include/lldb/Breakpoint/BreakpointIDList.h; sourceTree = ""; }; + 26BC7CF110F1B71400F91463 /* BreakpointList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointList.h; path = include/lldb/Breakpoint/BreakpointList.h; sourceTree = ""; }; + 26BC7CF210F1B71400F91463 /* BreakpointLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointLocation.h; path = include/lldb/Breakpoint/BreakpointLocation.h; sourceTree = ""; }; + 26BC7CF310F1B71400F91463 /* BreakpointLocationCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointLocationCollection.h; path = include/lldb/Breakpoint/BreakpointLocationCollection.h; sourceTree = ""; }; + 26BC7CF410F1B71400F91463 /* BreakpointLocationList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointLocationList.h; path = include/lldb/Breakpoint/BreakpointLocationList.h; sourceTree = ""; }; + 26BC7CF510F1B71400F91463 /* BreakpointOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointOptions.h; path = include/lldb/Breakpoint/BreakpointOptions.h; sourceTree = ""; }; + 26BC7CF610F1B71400F91463 /* BreakpointResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointResolver.h; path = include/lldb/Breakpoint/BreakpointResolver.h; sourceTree = ""; }; + 26BC7CF710F1B71400F91463 /* BreakpointSite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointSite.h; path = include/lldb/Breakpoint/BreakpointSite.h; sourceTree = ""; }; + 26BC7CF810F1B71400F91463 /* BreakpointSiteList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointSiteList.h; path = include/lldb/Breakpoint/BreakpointSiteList.h; sourceTree = ""; }; + 26BC7CF910F1B71400F91463 /* SearchFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SearchFilter.h; path = include/lldb/Core/SearchFilter.h; sourceTree = ""; }; + 26BC7CFA10F1B71400F91463 /* Stoppoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stoppoint.h; path = include/lldb/Breakpoint/Stoppoint.h; sourceTree = ""; }; + 26BC7CFB10F1B71400F91463 /* StoppointLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StoppointLocation.h; path = include/lldb/Breakpoint/StoppointLocation.h; sourceTree = ""; }; + 26BC7CFC10F1B71400F91463 /* WatchpointLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WatchpointLocation.h; path = include/lldb/Breakpoint/WatchpointLocation.h; sourceTree = ""; }; + 26BC7D1010F1B76300F91463 /* CommandObjectAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectAdd.h; path = source/Commands/CommandObjectAdd.h; sourceTree = ""; }; + 26BC7D1110F1B76300F91463 /* CommandObjectAlias.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectAlias.h; path = source/Commands/CommandObjectAlias.h; sourceTree = ""; }; + 26BC7D1210F1B76300F91463 /* CommandObjectAppend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectAppend.h; path = source/Commands/CommandObjectAppend.h; sourceTree = ""; }; + 26BC7D1410F1B76300F91463 /* CommandObjectBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectBreakpoint.h; path = source/Commands/CommandObjectBreakpoint.h; sourceTree = ""; }; + 26BC7D1610F1B76300F91463 /* CommandObjectDelete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectDelete.h; path = source/Commands/CommandObjectDelete.h; sourceTree = ""; }; + 26BC7D1710F1B76300F91463 /* CommandObjectDisassemble.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectDisassemble.h; path = source/Commands/CommandObjectDisassemble.h; sourceTree = ""; }; + 26BC7D1810F1B76300F91463 /* CommandObjectExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectExpression.h; path = source/Commands/CommandObjectExpression.h; sourceTree = ""; }; + 26BC7D1910F1B76300F91463 /* CommandObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectFile.h; path = source/Commands/CommandObjectFile.h; sourceTree = ""; }; + 26BC7D1A10F1B76300F91463 /* CommandObjectHelp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectHelp.h; path = source/Commands/CommandObjectHelp.h; sourceTree = ""; }; + 26BC7D1B10F1B76300F91463 /* CommandObjectImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectImage.h; path = source/Commands/CommandObjectImage.h; sourceTree = ""; }; + 26BC7D1C10F1B76300F91463 /* CommandObjectInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectInfo.h; path = source/Commands/CommandObjectInfo.h; sourceTree = ""; }; + 26BC7D1D10F1B76300F91463 /* CommandObjectMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectMemory.h; path = source/Commands/CommandObjectMemory.h; sourceTree = ""; }; + 26BC7D1F10F1B76300F91463 /* CommandObjectProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectProcess.h; path = source/Commands/CommandObjectProcess.h; sourceTree = ""; }; + 26BC7D2010F1B76300F91463 /* CommandObjectQuit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectQuit.h; path = source/Commands/CommandObjectQuit.h; sourceTree = ""; }; + 26BC7D2210F1B76300F91463 /* CommandObjectRegister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectRegister.h; path = source/Commands/CommandObjectRegister.h; sourceTree = ""; }; + 26BC7D2310F1B76300F91463 /* CommandObjectRemove.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectRemove.h; path = source/Commands/CommandObjectRemove.h; sourceTree = ""; }; + 26BC7D2410F1B76300F91463 /* CommandObjectScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectScript.h; path = source/Commands/CommandObjectScript.h; sourceTree = ""; }; + 26BC7D2510F1B76300F91463 /* CommandObjectSelect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSelect.h; path = source/Commands/CommandObjectSelect.h; sourceTree = ""; }; + 26BC7D2610F1B76300F91463 /* CommandObjectSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSet.h; path = source/Commands/CommandObjectSet.h; sourceTree = ""; }; + 26BC7D2710F1B76300F91463 /* CommandObjectSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSettings.h; path = source/Commands/CommandObjectSettings.h; sourceTree = ""; }; + 26BC7D2810F1B76300F91463 /* CommandObjectShow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectShow.h; path = source/Commands/CommandObjectShow.h; sourceTree = ""; }; + 26BC7D2910F1B76300F91463 /* CommandObjectSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSource.h; path = source/Commands/CommandObjectSource.h; sourceTree = ""; }; + 26BC7D2A10F1B76300F91463 /* CommandObjectSourceFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSourceFile.h; path = source/Commands/CommandObjectSourceFile.h; sourceTree = ""; }; + 26BC7D2B10F1B76300F91463 /* CommandObjectStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectStatus.h; path = source/Commands/CommandObjectStatus.h; sourceTree = ""; }; + 26BC7D2C10F1B76300F91463 /* CommandObjectSyntax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectSyntax.h; path = source/Commands/CommandObjectSyntax.h; sourceTree = ""; }; + 26BC7D2D10F1B76300F91463 /* CommandObjectThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectThread.h; path = source/Commands/CommandObjectThread.h; sourceTree = ""; }; + 26BC7D2E10F1B76300F91463 /* CommandObjectTranslate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectTranslate.h; path = source/Commands/CommandObjectTranslate.h; sourceTree = ""; }; + 26BC7D2F10F1B76300F91463 /* CommandObjectVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectVariable.h; path = source/Commands/CommandObjectVariable.h; sourceTree = ""; }; + 26BC7D5010F1B77400F91463 /* Address.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Address.h; path = include/lldb/Core/Address.h; sourceTree = ""; }; + 26BC7D5110F1B77400F91463 /* AddressRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddressRange.h; path = include/lldb/Core/AddressRange.h; sourceTree = ""; }; + 26BC7D5210F1B77400F91463 /* ArchSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ArchSpec.h; path = include/lldb/Core/ArchSpec.h; sourceTree = ""; }; + 26BC7D5310F1B77400F91463 /* Args.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Args.h; path = include/lldb/Core/Args.h; sourceTree = ""; }; + 26BC7D5410F1B77400F91463 /* Broadcaster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Broadcaster.h; path = include/lldb/Core/Broadcaster.h; sourceTree = ""; }; + 26BC7D5510F1B77400F91463 /* ClangForward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangForward.h; path = include/lldb/Core/ClangForward.h; sourceTree = ""; }; + 26BC7D5610F1B77400F91463 /* Communication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Communication.h; path = include/lldb/Core/Communication.h; sourceTree = ""; }; + 26BC7D5710F1B77400F91463 /* Connection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Connection.h; path = include/lldb/Core/Connection.h; sourceTree = ""; }; + 26BC7D5810F1B77400F91463 /* ConnectionFileDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConnectionFileDescriptor.h; path = include/lldb/Core/ConnectionFileDescriptor.h; sourceTree = ""; }; + 26BC7D5910F1B77400F91463 /* DataBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataBuffer.h; path = include/lldb/Core/DataBuffer.h; sourceTree = ""; }; + 26BC7D5A10F1B77400F91463 /* DataExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataExtractor.h; path = include/lldb/Core/DataExtractor.h; sourceTree = ""; }; + 26BC7D5B10F1B77400F91463 /* DataBufferHeap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataBufferHeap.h; path = include/lldb/Core/DataBufferHeap.h; sourceTree = ""; }; + 26BC7D5C10F1B77400F91463 /* DataBufferMemoryMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataBufferMemoryMap.h; path = include/lldb/Core/DataBufferMemoryMap.h; sourceTree = ""; }; + 26BC7D5D10F1B77400F91463 /* lldb-private-log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-private-log.h"; path = "include/lldb/lldb-private-log.h"; sourceTree = ""; }; + 26BC7D5E10F1B77400F91463 /* Disassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Disassembler.h; path = include/lldb/Core/Disassembler.h; sourceTree = ""; }; + 26BC7D5F10F1B77400F91463 /* dwarf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf.h; path = include/lldb/Core/dwarf.h; sourceTree = ""; }; + 26BC7D6010F1B77400F91463 /* Error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Error.h; path = include/lldb/Core/Error.h; sourceTree = ""; }; + 26BC7D6110F1B77400F91463 /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Event.h; path = include/lldb/Core/Event.h; sourceTree = ""; }; + 26BC7D6210F1B77400F91463 /* FileSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileSpec.h; path = include/lldb/Core/FileSpec.h; sourceTree = ""; }; + 26BC7D6310F1B77400F91463 /* FileSpecList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileSpecList.h; path = include/lldb/Core/FileSpecList.h; sourceTree = ""; }; + 26BC7D6410F1B77400F91463 /* Flags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Flags.h; path = include/lldb/Core/Flags.h; sourceTree = ""; }; + 26BC7D6510F1B77400F91463 /* IOStreamMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOStreamMacros.h; path = include/lldb/Core/IOStreamMacros.h; sourceTree = ""; }; + 26BC7D6610F1B77400F91463 /* Language.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Language.h; path = include/lldb/Core/Language.h; sourceTree = ""; }; + 26BC7D6710F1B77400F91463 /* Listener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Listener.h; path = include/lldb/Core/Listener.h; sourceTree = ""; }; + 26BC7D6810F1B77400F91463 /* Log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Log.h; path = include/lldb/Core/Log.h; sourceTree = ""; }; + 26BC7D6910F1B77400F91463 /* Mangled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mangled.h; path = include/lldb/Core/Mangled.h; sourceTree = ""; }; + 26BC7D6A10F1B77400F91463 /* Module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Module.h; path = include/lldb/Core/Module.h; sourceTree = ""; }; + 26BC7D6B10F1B77400F91463 /* ModuleChild.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ModuleChild.h; path = include/lldb/Core/ModuleChild.h; sourceTree = ""; }; + 26BC7D6C10F1B77400F91463 /* ModuleList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ModuleList.h; path = include/lldb/Core/ModuleList.h; sourceTree = ""; }; + 26BC7D6D10F1B77400F91463 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Options.h; path = include/lldb/Core/Options.h; sourceTree = ""; }; + 26BC7D7010F1B77400F91463 /* PluginInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PluginInterface.h; path = include/lldb/Core/PluginInterface.h; sourceTree = ""; }; + 26BC7D7110F1B77400F91463 /* PluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PluginManager.h; path = include/lldb/Core/PluginManager.h; sourceTree = ""; }; + 26BC7D7310F1B77400F91463 /* RegularExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegularExpression.h; path = include/lldb/Core/RegularExpression.h; sourceTree = ""; }; + 26BC7D7410F1B77400F91463 /* Scalar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Scalar.h; path = include/lldb/Core/Scalar.h; sourceTree = ""; }; + 26BC7D7510F1B77400F91463 /* Section.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Section.h; path = include/lldb/Core/Section.h; sourceTree = ""; }; + 26BC7D7610F1B77400F91463 /* SourceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SourceManager.h; path = include/lldb/Core/SourceManager.h; sourceTree = ""; }; + 26BC7D7710F1B77400F91463 /* State.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = State.h; path = include/lldb/Core/State.h; sourceTree = ""; }; + 26BC7D7810F1B77400F91463 /* STLUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STLUtils.h; path = include/lldb/Core/STLUtils.h; sourceTree = ""; }; + 26BC7D7910F1B77400F91463 /* Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stream.h; path = include/lldb/Core/Stream.h; sourceTree = ""; }; + 26BC7D7A10F1B77400F91463 /* StreamFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamFile.h; path = include/lldb/Core/StreamFile.h; sourceTree = ""; }; + 26BC7D7B10F1B77400F91463 /* StreamString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamString.h; path = include/lldb/Core/StreamString.h; sourceTree = ""; }; + 26BC7D7C10F1B77400F91463 /* ConstString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConstString.h; path = include/lldb/Core/ConstString.h; sourceTree = ""; }; + 26BC7D7E10F1B77400F91463 /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Timer.h; path = include/lldb/Core/Timer.h; sourceTree = ""; }; + 26BC7D7F10F1B77400F91463 /* TTYState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTYState.h; path = include/lldb/Core/TTYState.h; sourceTree = ""; }; + 26BC7D8010F1B77400F91463 /* UserID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UserID.h; path = include/lldb/Core/UserID.h; sourceTree = ""; }; + 26BC7D8110F1B77400F91463 /* Value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Value.h; path = include/lldb/Core/Value.h; sourceTree = ""; }; + 26BC7D8210F1B77400F91463 /* ValueObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObject.h; path = include/lldb/Core/ValueObject.h; sourceTree = ""; }; + 26BC7D8310F1B77400F91463 /* ValueObjectChild.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectChild.h; path = include/lldb/Core/ValueObjectChild.h; sourceTree = ""; }; + 26BC7D8410F1B77400F91463 /* ValueObjectList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectList.h; path = include/lldb/Core/ValueObjectList.h; sourceTree = ""; }; + 26BC7D8510F1B77400F91463 /* ValueObjectVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueObjectVariable.h; path = include/lldb/Core/ValueObjectVariable.h; sourceTree = ""; }; + 26BC7D8610F1B77400F91463 /* VMRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VMRange.h; path = include/lldb/Core/VMRange.h; sourceTree = ""; }; + 26BC7DC010F1B79500F91463 /* ClangExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpression.h; path = include/lldb/Expression/ClangExpression.h; sourceTree = ""; }; + 26BC7DC110F1B79500F91463 /* ClangExpressionVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpressionVariable.h; path = include/lldb/Expression/ClangExpressionVariable.h; sourceTree = ""; }; + 26BC7DC210F1B79500F91463 /* ClangStmtVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangStmtVisitor.h; path = include/lldb/Expression/ClangStmtVisitor.h; sourceTree = ""; }; + 26BC7DC310F1B79500F91463 /* DWARFExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DWARFExpression.h; path = include/lldb/Expression/DWARFExpression.h; sourceTree = ""; }; + 26BC7DD210F1B7D500F91463 /* Condition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Condition.h; path = include/lldb/Host/Condition.h; sourceTree = ""; }; + 26BC7DD310F1B7D500F91463 /* Endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Endian.h; path = include/lldb/Host/Endian.h; sourceTree = ""; }; + 26BC7DD410F1B7D500F91463 /* Host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Host.h; path = include/lldb/Host/Host.h; sourceTree = ""; }; + 26BC7DD510F1B7D500F91463 /* Mutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mutex.h; path = include/lldb/Host/Mutex.h; sourceTree = ""; }; + 26BC7DD610F1B7D500F91463 /* Predicate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Predicate.h; path = include/lldb/Host/Predicate.h; sourceTree = ""; }; + 26BC7DD710F1B7D500F91463 /* Types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Types.h; path = include/lldb/Host/Types.h; sourceTree = ""; }; + 26BC7DE110F1B7F900F91463 /* CommandContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandContext.h; path = include/lldb/Interpreter/CommandContext.h; sourceTree = ""; }; + 26BC7DE210F1B7F900F91463 /* CommandInterpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandInterpreter.h; path = include/lldb/Interpreter/CommandInterpreter.h; sourceTree = ""; }; + 26BC7DE310F1B7F900F91463 /* CommandObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObject.h; path = include/lldb/Interpreter/CommandObject.h; sourceTree = ""; }; + 26BC7DE410F1B7F900F91463 /* CommandReturnObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandReturnObject.h; path = include/lldb/Interpreter/CommandReturnObject.h; sourceTree = ""; }; + 26BC7DE510F1B7F900F91463 /* ScriptInterpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScriptInterpreter.h; path = include/lldb/Interpreter/ScriptInterpreter.h; sourceTree = ""; }; + 26BC7DE610F1B7F900F91463 /* ScriptInterpreterPython.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScriptInterpreterPython.h; path = include/lldb/Interpreter/ScriptInterpreterPython.h; sourceTree = ""; }; + 26BC7DE710F1B7F900F91463 /* StateVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StateVariable.h; path = include/lldb/Interpreter/StateVariable.h; sourceTree = ""; }; + 26BC7DF110F1B81A00F91463 /* DynamicLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DynamicLoader.h; path = include/lldb/Target/DynamicLoader.h; sourceTree = ""; }; + 26BC7DF210F1B81A00F91463 /* ExecutionContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExecutionContext.h; path = include/lldb/Target/ExecutionContext.h; sourceTree = ""; }; + 26BC7DF310F1B81A00F91463 /* Process.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Process.h; path = include/lldb/Target/Process.h; sourceTree = ""; }; + 26BC7DF410F1B81A00F91463 /* RegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContext.h; path = include/lldb/Target/RegisterContext.h; sourceTree = ""; }; + 26BC7DF510F1B81A00F91463 /* StackFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StackFrame.h; path = include/lldb/Target/StackFrame.h; sourceTree = ""; }; + 26BC7DF610F1B81A00F91463 /* StackFrameList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StackFrameList.h; path = include/lldb/Target/StackFrameList.h; sourceTree = ""; }; + 26BC7DF710F1B81A00F91463 /* StackID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StackID.h; path = include/lldb/Target/StackID.h; sourceTree = ""; }; + 26BC7DF810F1B81A00F91463 /* Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Target.h; path = include/lldb/Target/Target.h; sourceTree = ""; }; + 26BC7DF910F1B81A00F91463 /* TargetList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TargetList.h; path = include/lldb/Target/TargetList.h; sourceTree = ""; }; + 26BC7DFA10F1B81A00F91463 /* Thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Thread.h; path = include/lldb/Target/Thread.h; sourceTree = ""; }; + 26BC7DFB10F1B81A00F91463 /* ThreadList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadList.h; path = include/lldb/Target/ThreadList.h; sourceTree = ""; }; + 26BC7DFC10F1B81A00F91463 /* ThreadPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlan.h; path = include/lldb/Target/ThreadPlan.h; sourceTree = ""; }; + 26BC7E0910F1B83100F91463 /* StoppointCallbackContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StoppointCallbackContext.cpp; path = source/Breakpoint/StoppointCallbackContext.cpp; sourceTree = ""; }; + 26BC7E0A10F1B83100F91463 /* Breakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Breakpoint.cpp; path = source/Breakpoint/Breakpoint.cpp; sourceTree = ""; }; + 26BC7E0B10F1B83100F91463 /* BreakpointID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointID.cpp; path = source/Breakpoint/BreakpointID.cpp; sourceTree = ""; }; + 26BC7E0C10F1B83100F91463 /* BreakpointIDList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointIDList.cpp; path = source/Breakpoint/BreakpointIDList.cpp; sourceTree = ""; }; + 26BC7E0D10F1B83100F91463 /* BreakpointList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointList.cpp; path = source/Breakpoint/BreakpointList.cpp; sourceTree = ""; }; + 26BC7E0E10F1B83100F91463 /* BreakpointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointLocation.cpp; path = source/Breakpoint/BreakpointLocation.cpp; sourceTree = ""; }; + 26BC7E0F10F1B83100F91463 /* BreakpointLocationCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointLocationCollection.cpp; path = source/Breakpoint/BreakpointLocationCollection.cpp; sourceTree = ""; }; + 26BC7E1010F1B83100F91463 /* BreakpointLocationList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointLocationList.cpp; path = source/Breakpoint/BreakpointLocationList.cpp; sourceTree = ""; }; + 26BC7E1110F1B83100F91463 /* BreakpointOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointOptions.cpp; path = source/Breakpoint/BreakpointOptions.cpp; sourceTree = ""; }; + 26BC7E1210F1B83100F91463 /* BreakpointResolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointResolver.cpp; path = source/Breakpoint/BreakpointResolver.cpp; sourceTree = ""; }; + 26BC7E1310F1B83100F91463 /* BreakpointSite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointSite.cpp; path = source/Breakpoint/BreakpointSite.cpp; sourceTree = ""; }; + 26BC7E1410F1B83100F91463 /* BreakpointSiteList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointSiteList.cpp; path = source/Breakpoint/BreakpointSiteList.cpp; sourceTree = ""; }; + 26BC7E1510F1B83100F91463 /* SearchFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SearchFilter.cpp; path = source/Core/SearchFilter.cpp; sourceTree = ""; }; + 26BC7E1610F1B83100F91463 /* Stoppoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Stoppoint.cpp; path = source/Breakpoint/Stoppoint.cpp; sourceTree = ""; }; + 26BC7E1710F1B83100F91463 /* StoppointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StoppointLocation.cpp; path = source/Breakpoint/StoppointLocation.cpp; sourceTree = ""; }; + 26BC7E1810F1B83100F91463 /* WatchpointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WatchpointLocation.cpp; path = source/Breakpoint/WatchpointLocation.cpp; sourceTree = ""; }; + 26BC7E2910F1B84700F91463 /* CommandObjectAdd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectAdd.cpp; path = source/Commands/CommandObjectAdd.cpp; sourceTree = ""; }; + 26BC7E2A10F1B84700F91463 /* CommandObjectAlias.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectAlias.cpp; path = source/Commands/CommandObjectAlias.cpp; sourceTree = ""; }; + 26BC7E2B10F1B84700F91463 /* CommandObjectAppend.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectAppend.cpp; path = source/Commands/CommandObjectAppend.cpp; sourceTree = ""; }; + 26BC7E2D10F1B84700F91463 /* CommandObjectBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectBreakpoint.cpp; path = source/Commands/CommandObjectBreakpoint.cpp; sourceTree = ""; }; + 26BC7E2F10F1B84700F91463 /* CommandObjectDelete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectDelete.cpp; path = source/Commands/CommandObjectDelete.cpp; sourceTree = ""; }; + 26BC7E3010F1B84700F91463 /* CommandObjectDisassemble.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectDisassemble.cpp; path = source/Commands/CommandObjectDisassemble.cpp; sourceTree = ""; }; + 26BC7E3110F1B84700F91463 /* CommandObjectExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectExpression.cpp; path = source/Commands/CommandObjectExpression.cpp; sourceTree = ""; }; + 26BC7E3210F1B84700F91463 /* CommandObjectFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectFile.cpp; path = source/Commands/CommandObjectFile.cpp; sourceTree = ""; }; + 26BC7E3310F1B84700F91463 /* CommandObjectHelp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectHelp.cpp; path = source/Commands/CommandObjectHelp.cpp; sourceTree = ""; }; + 26BC7E3410F1B84700F91463 /* CommandObjectImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectImage.cpp; path = source/Commands/CommandObjectImage.cpp; sourceTree = ""; }; + 26BC7E3510F1B84700F91463 /* CommandObjectInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectInfo.cpp; path = source/Commands/CommandObjectInfo.cpp; sourceTree = ""; }; + 26BC7E3610F1B84700F91463 /* CommandObjectMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectMemory.cpp; path = source/Commands/CommandObjectMemory.cpp; sourceTree = ""; }; + 26BC7E3810F1B84700F91463 /* CommandObjectProcess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectProcess.cpp; path = source/Commands/CommandObjectProcess.cpp; sourceTree = ""; }; + 26BC7E3910F1B84700F91463 /* CommandObjectQuit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectQuit.cpp; path = source/Commands/CommandObjectQuit.cpp; sourceTree = ""; }; + 26BC7E3B10F1B84700F91463 /* CommandObjectRegister.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectRegister.cpp; path = source/Commands/CommandObjectRegister.cpp; sourceTree = ""; }; + 26BC7E3C10F1B84700F91463 /* CommandObjectRemove.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectRemove.cpp; path = source/Commands/CommandObjectRemove.cpp; sourceTree = ""; }; + 26BC7E3D10F1B84700F91463 /* CommandObjectScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectScript.cpp; path = source/Commands/CommandObjectScript.cpp; sourceTree = ""; }; + 26BC7E3E10F1B84700F91463 /* CommandObjectSelect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSelect.cpp; path = source/Commands/CommandObjectSelect.cpp; sourceTree = ""; }; + 26BC7E3F10F1B84700F91463 /* CommandObjectSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSet.cpp; path = source/Commands/CommandObjectSet.cpp; sourceTree = ""; }; + 26BC7E4010F1B84700F91463 /* CommandObjectSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSettings.cpp; path = source/Commands/CommandObjectSettings.cpp; sourceTree = ""; }; + 26BC7E4110F1B84700F91463 /* CommandObjectShow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectShow.cpp; path = source/Commands/CommandObjectShow.cpp; sourceTree = ""; }; + 26BC7E4210F1B84700F91463 /* CommandObjectSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSource.cpp; path = source/Commands/CommandObjectSource.cpp; sourceTree = ""; }; + 26BC7E4310F1B84700F91463 /* CommandObjectSourceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSourceFile.cpp; path = source/Commands/CommandObjectSourceFile.cpp; sourceTree = ""; }; + 26BC7E4410F1B84700F91463 /* CommandObjectStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectStatus.cpp; path = source/Commands/CommandObjectStatus.cpp; sourceTree = ""; }; + 26BC7E4510F1B84700F91463 /* CommandObjectSyntax.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectSyntax.cpp; path = source/Commands/CommandObjectSyntax.cpp; sourceTree = ""; }; + 26BC7E4610F1B84700F91463 /* CommandObjectThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectThread.cpp; path = source/Commands/CommandObjectThread.cpp; sourceTree = ""; }; + 26BC7E4710F1B84700F91463 /* CommandObjectTranslate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectTranslate.cpp; path = source/Commands/CommandObjectTranslate.cpp; sourceTree = ""; }; + 26BC7E4810F1B84700F91463 /* CommandObjectVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectVariable.cpp; path = source/Commands/CommandObjectVariable.cpp; sourceTree = ""; }; + 26BC7E6910F1B85900F91463 /* Address.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Address.cpp; path = source/Core/Address.cpp; sourceTree = ""; }; + 26BC7E6A10F1B85900F91463 /* AddressRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddressRange.cpp; path = source/Core/AddressRange.cpp; sourceTree = ""; }; + 26BC7E6B10F1B85900F91463 /* ArchSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ArchSpec.cpp; path = source/Core/ArchSpec.cpp; sourceTree = ""; }; + 26BC7E6C10F1B85900F91463 /* Args.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Args.cpp; path = source/Core/Args.cpp; sourceTree = ""; }; + 26BC7E6D10F1B85900F91463 /* Broadcaster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Broadcaster.cpp; path = source/Core/Broadcaster.cpp; sourceTree = ""; }; + 26BC7E6E10F1B85900F91463 /* Communication.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Communication.cpp; path = source/Core/Communication.cpp; sourceTree = ""; }; + 26BC7E6F10F1B85900F91463 /* Connection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Connection.cpp; path = source/Core/Connection.cpp; sourceTree = ""; }; + 26BC7E7010F1B85900F91463 /* ConnectionFileDescriptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConnectionFileDescriptor.cpp; path = source/Core/ConnectionFileDescriptor.cpp; sourceTree = ""; }; + 26BC7E7110F1B85900F91463 /* DataExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataExtractor.cpp; path = source/Core/DataExtractor.cpp; sourceTree = ""; }; + 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataBufferHeap.cpp; path = source/Core/DataBufferHeap.cpp; sourceTree = ""; }; + 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataBufferMemoryMap.cpp; path = source/Core/DataBufferMemoryMap.cpp; sourceTree = ""; }; + 26BC7E7410F1B85900F91463 /* lldb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lldb.cpp; path = source/lldb.cpp; sourceTree = ""; }; + 26BC7E7510F1B85900F91463 /* lldb-log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lldb-log.cpp"; path = "source/lldb-log.cpp"; sourceTree = ""; }; + 26BC7E7610F1B85900F91463 /* Disassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler.cpp; path = source/Core/Disassembler.cpp; sourceTree = ""; }; + 26BC7E7710F1B85900F91463 /* DynamicLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DynamicLoader.cpp; path = source/Core/DynamicLoader.cpp; sourceTree = ""; }; + 26BC7E7810F1B85900F91463 /* Error.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Error.cpp; path = source/Core/Error.cpp; sourceTree = ""; }; + 26BC7E7910F1B85900F91463 /* Event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Event.cpp; path = source/Core/Event.cpp; sourceTree = ""; }; + 26BC7E7A10F1B85900F91463 /* FileSpec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileSpec.cpp; path = source/Core/FileSpec.cpp; sourceTree = ""; }; + 26BC7E7B10F1B85900F91463 /* FileSpecList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileSpecList.cpp; path = source/Core/FileSpecList.cpp; sourceTree = ""; }; + 26BC7E7C10F1B85900F91463 /* Flags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Flags.cpp; path = source/Core/Flags.cpp; sourceTree = ""; }; + 26BC7E7D10F1B85900F91463 /* Language.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Language.cpp; path = source/Core/Language.cpp; sourceTree = ""; }; + 26BC7E7E10F1B85900F91463 /* Listener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Listener.cpp; path = source/Core/Listener.cpp; sourceTree = ""; }; + 26BC7E7F10F1B85900F91463 /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Log.cpp; path = source/Core/Log.cpp; sourceTree = ""; }; + 26BC7E8010F1B85900F91463 /* Mangled.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mangled.cpp; path = source/Core/Mangled.cpp; sourceTree = ""; }; + 26BC7E8110F1B85900F91463 /* Module.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Module.cpp; path = source/Core/Module.cpp; sourceTree = ""; }; + 26BC7E8210F1B85900F91463 /* ModuleChild.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ModuleChild.cpp; path = source/Core/ModuleChild.cpp; sourceTree = ""; }; + 26BC7E8310F1B85900F91463 /* ModuleList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ModuleList.cpp; path = source/Core/ModuleList.cpp; sourceTree = ""; }; + 26BC7E8610F1B85900F91463 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = source/Core/Options.cpp; sourceTree = ""; }; + 26BC7E8A10F1B85900F91463 /* PluginManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PluginManager.cpp; path = source/Core/PluginManager.cpp; sourceTree = ""; }; + 26BC7E8C10F1B85900F91463 /* RegularExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegularExpression.cpp; path = source/Core/RegularExpression.cpp; sourceTree = ""; }; + 26BC7E8D10F1B85900F91463 /* Scalar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Scalar.cpp; path = source/Core/Scalar.cpp; sourceTree = ""; }; + 26BC7E8E10F1B85900F91463 /* Section.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Section.cpp; path = source/Core/Section.cpp; sourceTree = ""; }; + 26BC7E8F10F1B85900F91463 /* SourceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SourceManager.cpp; path = source/Core/SourceManager.cpp; sourceTree = ""; }; + 26BC7E9010F1B85900F91463 /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = State.cpp; path = source/Core/State.cpp; sourceTree = ""; }; + 26BC7E9110F1B85900F91463 /* Stream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Stream.cpp; path = source/Core/Stream.cpp; sourceTree = ""; }; + 26BC7E9210F1B85900F91463 /* StreamFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamFile.cpp; path = source/Core/StreamFile.cpp; sourceTree = ""; }; + 26BC7E9310F1B85900F91463 /* StreamString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamString.cpp; path = source/Core/StreamString.cpp; sourceTree = ""; }; + 26BC7E9410F1B85900F91463 /* ConstString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConstString.cpp; path = source/Core/ConstString.cpp; sourceTree = ""; }; + 26BC7E9610F1B85900F91463 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Timer.cpp; path = source/Core/Timer.cpp; sourceTree = ""; }; + 26BC7E9710F1B85900F91463 /* TTYState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TTYState.cpp; path = source/Core/TTYState.cpp; sourceTree = ""; }; + 26BC7E9810F1B85900F91463 /* UserID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UserID.cpp; path = source/Core/UserID.cpp; sourceTree = ""; }; + 26BC7E9910F1B85900F91463 /* Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Value.cpp; path = source/Core/Value.cpp; sourceTree = ""; }; + 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObject.cpp; path = source/Core/ValueObject.cpp; sourceTree = ""; }; + 26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectChild.cpp; path = source/Core/ValueObjectChild.cpp; sourceTree = ""; }; + 26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectList.cpp; path = source/Core/ValueObjectList.cpp; sourceTree = ""; }; + 26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectVariable.cpp; path = source/Core/ValueObjectVariable.cpp; sourceTree = ""; }; + 26BC7E9E10F1B85900F91463 /* VMRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VMRange.cpp; path = source/Core/VMRange.cpp; sourceTree = ""; }; + 26BC7ED510F1B86700F91463 /* ClangExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExpression.cpp; path = source/Expression/ClangExpression.cpp; sourceTree = ""; }; + 26BC7ED610F1B86700F91463 /* ClangExpressionVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExpressionVariable.cpp; path = source/Expression/ClangExpressionVariable.cpp; sourceTree = ""; }; + 26BC7ED710F1B86700F91463 /* ClangStmtVisitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangStmtVisitor.cpp; path = source/Expression/ClangStmtVisitor.cpp; sourceTree = ""; }; + 26BC7ED810F1B86700F91463 /* DWARFExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DWARFExpression.cpp; path = source/Expression/DWARFExpression.cpp; sourceTree = ""; }; + 26BC7EE710F1B88F00F91463 /* Condition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Condition.cpp; path = source/Host/macosx/Condition.cpp; sourceTree = ""; }; + 26BC7EE810F1B88F00F91463 /* Host.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Host.mm; path = source/Host/macosx/Host.mm; sourceTree = ""; }; + 26BC7EE910F1B88F00F91463 /* Mutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mutex.cpp; path = source/Host/macosx/Mutex.cpp; sourceTree = ""; }; + 26BC7EED10F1B8AD00F91463 /* CFCBundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFCBundle.cpp; path = source/Host/macosx/cfcpp/CFCBundle.cpp; sourceTree = ""; }; + 26BC7EEE10F1B8AD00F91463 /* CFCBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCBundle.h; path = source/Host/macosx/cfcpp/CFCBundle.h; sourceTree = ""; }; + 26BC7EEF10F1B8AD00F91463 /* CFCData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFCData.cpp; path = source/Host/macosx/cfcpp/CFCData.cpp; sourceTree = ""; }; + 26BC7EF010F1B8AD00F91463 /* CFCData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCData.h; path = source/Host/macosx/cfcpp/CFCData.h; sourceTree = ""; }; + 26BC7EF110F1B8AD00F91463 /* CFCMutableArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFCMutableArray.cpp; path = source/Host/macosx/cfcpp/CFCMutableArray.cpp; sourceTree = ""; }; + 26BC7EF210F1B8AD00F91463 /* CFCMutableArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCMutableArray.h; path = source/Host/macosx/cfcpp/CFCMutableArray.h; sourceTree = ""; }; + 26BC7EF310F1B8AD00F91463 /* CFCMutableDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFCMutableDictionary.cpp; path = source/Host/macosx/cfcpp/CFCMutableDictionary.cpp; sourceTree = ""; }; + 26BC7EF410F1B8AD00F91463 /* CFCMutableDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCMutableDictionary.h; path = source/Host/macosx/cfcpp/CFCMutableDictionary.h; sourceTree = ""; }; + 26BC7EF510F1B8AD00F91463 /* CFCMutableSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFCMutableSet.cpp; path = source/Host/macosx/cfcpp/CFCMutableSet.cpp; sourceTree = ""; }; + 26BC7EF610F1B8AD00F91463 /* CFCMutableSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCMutableSet.h; path = source/Host/macosx/cfcpp/CFCMutableSet.h; sourceTree = ""; }; + 26BC7EF710F1B8AD00F91463 /* CFCReleaser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCReleaser.h; path = source/Host/macosx/cfcpp/CFCReleaser.h; sourceTree = ""; }; + 26BC7EF810F1B8AD00F91463 /* CFCString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFCString.cpp; path = source/Host/macosx/cfcpp/CFCString.cpp; sourceTree = ""; }; + 26BC7EF910F1B8AD00F91463 /* CFCString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFCString.h; path = source/Host/macosx/cfcpp/CFCString.h; sourceTree = ""; }; + 26BC7F0710F1B8DD00F91463 /* CommandContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandContext.cpp; path = source/Interpreter/CommandContext.cpp; sourceTree = ""; }; + 26BC7F0810F1B8DD00F91463 /* CommandInterpreter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandInterpreter.cpp; path = source/Interpreter/CommandInterpreter.cpp; sourceTree = ""; }; + 26BC7F0910F1B8DD00F91463 /* CommandObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObject.cpp; path = source/Interpreter/CommandObject.cpp; sourceTree = ""; }; + 26BC7F0A10F1B8DD00F91463 /* CommandReturnObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandReturnObject.cpp; path = source/Interpreter/CommandReturnObject.cpp; sourceTree = ""; }; + 26BC7F0B10F1B8DD00F91463 /* StateVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StateVariable.cpp; path = source/Interpreter/StateVariable.cpp; sourceTree = ""; }; + 26BC7F0C10F1B8DD00F91463 /* ScriptInterpreterPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScriptInterpreterPython.cpp; path = source/Interpreter/ScriptInterpreterPython.cpp; sourceTree = ""; }; + 26BC7F1310F1B8EC00F91463 /* Block.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Block.cpp; path = source/Symbol/Block.cpp; sourceTree = ""; }; + 26BC7F1410F1B8EC00F91463 /* ClangASTContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangASTContext.cpp; path = source/Symbol/ClangASTContext.cpp; sourceTree = ""; }; + 26BC7F1510F1B8EC00F91463 /* CompileUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompileUnit.cpp; path = source/Symbol/CompileUnit.cpp; sourceTree = ""; }; + 26BC7F1610F1B8EC00F91463 /* Declaration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Declaration.cpp; path = source/Symbol/Declaration.cpp; sourceTree = ""; }; + 26BC7F1710F1B8EC00F91463 /* DWARFCallFrameInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DWARFCallFrameInfo.cpp; path = source/Symbol/DWARFCallFrameInfo.cpp; sourceTree = ""; }; + 26BC7F1810F1B8EC00F91463 /* Function.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Function.cpp; path = source/Symbol/Function.cpp; sourceTree = ""; }; + 26BC7F1910F1B8EC00F91463 /* LineEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LineEntry.cpp; path = source/Symbol/LineEntry.cpp; sourceTree = ""; }; + 26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LineTable.cpp; path = source/Symbol/LineTable.cpp; sourceTree = ""; }; + 26BC7F1B10F1B8EC00F91463 /* Symbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Symbol.cpp; path = source/Symbol/Symbol.cpp; sourceTree = ""; }; + 26BC7F1C10F1B8EC00F91463 /* SymbolContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolContext.cpp; path = source/Symbol/SymbolContext.cpp; sourceTree = ""; }; + 26BC7F1D10F1B8EC00F91463 /* SymbolFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolFile.cpp; path = source/Symbol/SymbolFile.cpp; sourceTree = ""; }; + 26BC7F1E10F1B8EC00F91463 /* SymbolVendor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SymbolVendor.mm; path = source/Symbol/SymbolVendor.mm; sourceTree = ""; }; + 26BC7F1F10F1B8EC00F91463 /* Symtab.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Symtab.cpp; path = source/Symbol/Symtab.cpp; sourceTree = ""; }; + 26BC7F2010F1B8EC00F91463 /* Type.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Type.cpp; path = source/Symbol/Type.cpp; sourceTree = ""; }; + 26BC7F2110F1B8EC00F91463 /* TypeList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeList.cpp; path = source/Symbol/TypeList.cpp; sourceTree = ""; }; + 26BC7F2210F1B8EC00F91463 /* Variable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Variable.cpp; path = source/Symbol/Variable.cpp; sourceTree = ""; }; + 26BC7F2310F1B8EC00F91463 /* VariableList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VariableList.cpp; path = source/Symbol/VariableList.cpp; sourceTree = ""; }; + 26BC7F3510F1B90C00F91463 /* ExecutionContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExecutionContext.cpp; path = source/Target/ExecutionContext.cpp; sourceTree = ""; }; + 26BC7F3610F1B90C00F91463 /* Process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Process.cpp; path = source/Target/Process.cpp; sourceTree = ""; }; + 26BC7F3710F1B90C00F91463 /* RegisterContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContext.cpp; path = source/Target/RegisterContext.cpp; sourceTree = ""; }; + 26BC7F3810F1B90C00F91463 /* StackFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StackFrame.cpp; path = source/Target/StackFrame.cpp; sourceTree = ""; }; + 26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StackFrameList.cpp; path = source/Target/StackFrameList.cpp; sourceTree = ""; }; + 26BC7F3A10F1B90C00F91463 /* StackID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StackID.cpp; path = source/Target/StackID.cpp; sourceTree = ""; }; + 26BC7F3B10F1B90C00F91463 /* Target.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Target.cpp; path = source/Target/Target.cpp; sourceTree = ""; }; + 26BC7F3C10F1B90C00F91463 /* TargetList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TargetList.cpp; path = source/Target/TargetList.cpp; sourceTree = ""; }; + 26BC7F3D10F1B90C00F91463 /* Thread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Thread.cpp; path = source/Target/Thread.cpp; sourceTree = ""; }; + 26BC7F3E10F1B90C00F91463 /* ThreadList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadList.cpp; path = source/Target/ThreadList.cpp; sourceTree = ""; }; + 26BC7F3F10F1B90C00F91463 /* ThreadPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlan.cpp; path = source/Target/ThreadPlan.cpp; sourceTree = ""; }; + 26BC7F4C10F1BC1A00F91463 /* ObjectFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectFile.cpp; path = source/Symbol/ObjectFile.cpp; sourceTree = ""; }; + 26C81CA411335651004BDC5A /* UUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UUID.h; path = include/lldb/Core/UUID.h; sourceTree = ""; }; + 26C81CA511335651004BDC5A /* UUID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UUID.cpp; path = source/Core/UUID.cpp; sourceTree = ""; }; + 26C9DF03113C5B93006B0F94 /* lldb */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lldb; path = include/lldb; sourceTree = ""; }; + 26D0DD5010FE554D00271C65 /* BreakpointResolverAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointResolverAddress.h; path = include/lldb/Breakpoint/BreakpointResolverAddress.h; sourceTree = ""; }; + 26D0DD5110FE554D00271C65 /* BreakpointResolverFileLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointResolverFileLine.h; path = include/lldb/Breakpoint/BreakpointResolverFileLine.h; sourceTree = ""; }; + 26D0DD5210FE554D00271C65 /* BreakpointResolverName.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointResolverName.h; path = include/lldb/Breakpoint/BreakpointResolverName.h; sourceTree = ""; }; + 26D0DD5310FE555900271C65 /* BreakpointResolverAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointResolverAddress.cpp; path = source/Breakpoint/BreakpointResolverAddress.cpp; sourceTree = ""; }; + 26D0DD5410FE555900271C65 /* BreakpointResolverFileLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointResolverFileLine.cpp; path = source/Breakpoint/BreakpointResolverFileLine.cpp; sourceTree = ""; }; + 26D0DD5510FE555900271C65 /* BreakpointResolverName.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BreakpointResolverName.cpp; path = source/Breakpoint/BreakpointResolverName.cpp; sourceTree = ""; }; + 26DAFD9711529BC7005A394E /* ExecutionContextScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExecutionContextScope.h; path = include/lldb/Target/ExecutionContextScope.h; sourceTree = ""; }; + 26DE1E6911616C2E00A093E2 /* lldb-forward-rtti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-forward-rtti.h"; path = "include/lldb/lldb-forward-rtti.h"; sourceTree = ""; }; + 26DE1E6A11616C2E00A093E2 /* lldb-forward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lldb-forward.h"; path = "include/lldb/lldb-forward.h"; sourceTree = ""; }; + 26DE204011618AB900A093E2 /* SBSymbolContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBSymbolContext.h; path = include/lldb/API/SBSymbolContext.h; sourceTree = ""; }; + 26DE204211618ACA00A093E2 /* SBAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBAddress.h; path = include/lldb/API/SBAddress.h; sourceTree = ""; }; + 26DE204411618ADA00A093E2 /* SBAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBAddress.cpp; path = source/API/SBAddress.cpp; sourceTree = ""; }; + 26DE204611618AED00A093E2 /* SBSymbolContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBSymbolContext.cpp; path = source/API/SBSymbolContext.cpp; sourceTree = ""; }; + 26DE204C11618E7A00A093E2 /* SBModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBModule.cpp; path = source/API/SBModule.cpp; sourceTree = ""; }; + 26DE204E11618E9800A093E2 /* SBModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBModule.h; path = include/lldb/API/SBModule.h; sourceTree = ""; }; + 26DE205211618FAC00A093E2 /* SBFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBFunction.h; path = include/lldb/API/SBFunction.h; sourceTree = ""; }; + 26DE205411618FB800A093E2 /* SBCompileUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBCompileUnit.h; path = include/lldb/API/SBCompileUnit.h; sourceTree = ""; }; + 26DE205611618FC500A093E2 /* SBBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBBlock.h; path = include/lldb/API/SBBlock.h; sourceTree = ""; }; + 26DE205811618FE700A093E2 /* SBLineEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBLineEntry.h; path = include/lldb/API/SBLineEntry.h; sourceTree = ""; }; + 26DE205A11618FF600A093E2 /* SBSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBSymbol.h; path = include/lldb/API/SBSymbol.h; sourceTree = ""; }; + 26DE205C1161901400A093E2 /* SBFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBFunction.cpp; path = source/API/SBFunction.cpp; sourceTree = ""; }; + 26DE205E1161901B00A093E2 /* SBCompileUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBCompileUnit.cpp; path = source/API/SBCompileUnit.cpp; sourceTree = ""; }; + 26DE20601161902600A093E2 /* SBBlock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBlock.cpp; path = source/API/SBBlock.cpp; sourceTree = ""; }; + 26DE20621161904200A093E2 /* SBLineEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBLineEntry.cpp; path = source/API/SBLineEntry.cpp; sourceTree = ""; }; + 26DE20641161904E00A093E2 /* SBSymbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBSymbol.cpp; path = source/API/SBSymbol.cpp; sourceTree = ""; }; + 26DFBC50113B48D600DD817F /* CommandObjectCrossref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectCrossref.h; path = include/lldb/Interpreter/CommandObjectCrossref.h; sourceTree = ""; }; + 26DFBC51113B48D600DD817F /* CommandObjectMultiword.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectMultiword.h; path = include/lldb/Interpreter/CommandObjectMultiword.h; sourceTree = ""; }; + 26DFBC52113B48D600DD817F /* CommandObjectRegexCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectRegexCommand.h; path = include/lldb/Interpreter/CommandObjectRegexCommand.h; sourceTree = ""; }; + 26DFBC57113B48F300DD817F /* CommandObjectCrossref.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectCrossref.cpp; path = source/Interpreter/CommandObjectCrossref.cpp; sourceTree = ""; }; + 26DFBC58113B48F300DD817F /* CommandObjectMultiword.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectMultiword.cpp; path = source/Interpreter/CommandObjectMultiword.cpp; sourceTree = ""; }; + 26DFBC59113B48F300DD817F /* CommandObjectRegexCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectRegexCommand.cpp; path = source/Interpreter/CommandObjectRegexCommand.cpp; sourceTree = ""; }; + 26E3EEBD11A9870400FBADB6 /* Unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Unwind.h; path = include/lldb/Target/Unwind.h; sourceTree = ""; }; + 26E3EEBE11A98A1900FBADB6 /* UnwindLibUnwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindLibUnwind.cpp; path = Utility/UnwindLibUnwind.cpp; sourceTree = ""; }; + 26E3EEBF11A98A1900FBADB6 /* UnwindLibUnwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnwindLibUnwind.h; path = Utility/UnwindLibUnwind.h; sourceTree = ""; }; + 26E3EEE311A9901300FBADB6 /* UnwindMacOSXFrameBackchain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindMacOSXFrameBackchain.cpp; path = Utility/UnwindMacOSXFrameBackchain.cpp; sourceTree = ""; }; + 26E3EEE411A9901300FBADB6 /* UnwindMacOSXFrameBackchain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnwindMacOSXFrameBackchain.h; path = Utility/UnwindMacOSXFrameBackchain.h; sourceTree = ""; }; + 26E3EEF711A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextMacOSXFrameBackchain.cpp; path = Utility/RegisterContextMacOSXFrameBackchain.cpp; sourceTree = ""; }; + 26E3EEF811A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextMacOSXFrameBackchain.h; path = Utility/RegisterContextMacOSXFrameBackchain.h; sourceTree = ""; }; + 26F5C26A10F3D9A4009D5894 /* lldb */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lldb; sourceTree = BUILT_PRODUCTS_DIR; }; + 26F5C27210F3D9E4009D5894 /* lldb-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "lldb-Info.plist"; path = "tools/driver/lldb-Info.plist"; sourceTree = ""; }; + 26F5C27310F3D9E4009D5894 /* Driver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Driver.cpp; path = tools/driver/Driver.cpp; sourceTree = ""; }; + 26F5C27410F3D9E4009D5894 /* Driver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Driver.h; path = tools/driver/Driver.h; sourceTree = ""; }; + 26F5C27510F3D9E4009D5894 /* IOChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IOChannel.cpp; path = tools/driver/IOChannel.cpp; sourceTree = ""; }; + 26F5C27610F3D9E4009D5894 /* IOChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOChannel.h; path = tools/driver/IOChannel.h; sourceTree = ""; }; + 26F5C32410F3DF23009D5894 /* libpython2.6.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpython2.6.dylib; path = /usr/lib/libpython2.6.dylib; sourceTree = ""; }; + 26F5C32A10F3DFDD009D5894 /* libedit.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libedit.dylib; path = /usr/lib/libedit.dylib; sourceTree = ""; }; + 26F5C32B10F3DFDD009D5894 /* libtermcap.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libtermcap.dylib; path = /usr/lib/libtermcap.dylib; sourceTree = ""; }; + 26F5C37410F3F61B009D5894 /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = /usr/lib/libobjc.dylib; sourceTree = ""; }; + 26F5C39010F3FA26009D5894 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + 26F996A7119B79C300412154 /* ARM_DWARF_Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARM_DWARF_Registers.h; path = source/Utility/ARM_DWARF_Registers.h; sourceTree = ""; }; + 26F996A8119B79C300412154 /* ARM_GCC_Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARM_GCC_Registers.h; path = source/Utility/ARM_GCC_Registers.h; sourceTree = ""; }; + 26FE25221146CADE00F4085A /* GDBRemoteCommunication.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GDBRemoteCommunication.cpp; path = "source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp"; sourceTree = ""; }; + 26FE25231146CADE00F4085A /* GDBRemoteCommunication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GDBRemoteCommunication.h; path = "source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h"; sourceTree = ""; }; + 493C63F01189203300914D5E /* ABISysV_x86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABISysV_x86_64.h; path = "ABI/SysV-x86_64/ABISysV_x86_64.h"; sourceTree = ""; }; + 493C63F11189203300914D5E /* ABISysV_x86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABISysV_x86_64.cpp; path = "ABI/SysV-x86_64/ABISysV_x86_64.cpp"; sourceTree = ""; }; + 495BBACB119A0DBE00418BEA /* PathMappingList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathMappingList.cpp; path = source/Target/PathMappingList.cpp; sourceTree = ""; }; + 495BBACF119A0DE700418BEA /* PathMappingList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathMappingList.h; path = include/lldb/Target/PathMappingList.h; sourceTree = ""; }; + 497650CE11A21BEE008DDB57 /* ABIMacOSX_i386.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABIMacOSX_i386.cpp; path = "ABI/MacOSX-i386/ABIMacOSX_i386.cpp"; sourceTree = ""; }; + 497650CF11A21BEE008DDB57 /* ABIMacOSX_i386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABIMacOSX_i386.h; path = "ABI/MacOSX-i386/ABIMacOSX_i386.h"; sourceTree = ""; }; + 497E7B331188ED300065CCA1 /* ABI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ABI.h; path = include/lldb/Target/ABI.h; sourceTree = ""; }; + 497E7B9D1188F6690065CCA1 /* ABI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ABI.cpp; path = source/Target/ABI.cpp; sourceTree = ""; }; + 499F381E11A5B3F300F5CE02 /* CommandObjectArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectArgs.h; path = source/Commands/CommandObjectArgs.h; sourceTree = ""; }; + 499F381F11A5B3F300F5CE02 /* CommandObjectArgs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectArgs.cpp; path = source/Commands/CommandObjectArgs.cpp; sourceTree = ""; }; + 49BF48DC11ADF356008863BD /* ObjCObjectPrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ObjCObjectPrinter.cpp; path = source/Target/ObjCObjectPrinter.cpp; sourceTree = ""; }; + 49BF48E011ADF37D008863BD /* ObjCObjectPrinter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjCObjectPrinter.h; path = include/lldb/Target/ObjCObjectPrinter.h; sourceTree = ""; }; + 49D7072611B5AD03001AD875 /* ClangASTSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangASTSource.h; path = include/lldb/Expression/ClangASTSource.h; sourceTree = ""; }; + 49D7072811B5AD11001AD875 /* ClangASTSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangASTSource.cpp; path = source/Expression/ClangASTSource.cpp; sourceTree = ""; }; + 49EC3E98118F90AC00B1265E /* ThreadPlanCallFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallFunction.cpp; path = source/Target/ThreadPlanCallFunction.cpp; sourceTree = ""; }; + 49EC3E9C118F90D400B1265E /* ThreadPlanCallFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanCallFunction.h; path = include/lldb/Target/ThreadPlanCallFunction.h; sourceTree = ""; }; + 49F1A74511B3388F003ED505 /* ClangExpressionDeclMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExpressionDeclMap.cpp; path = source/Expression/ClangExpressionDeclMap.cpp; sourceTree = ""; }; + 49F1A74911B338AE003ED505 /* ClangExpressionDeclMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangExpressionDeclMap.h; path = include/lldb/Expression/ClangExpressionDeclMap.h; sourceTree = ""; }; + 4C00986F11500B4300F316B0 /* UnixSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnixSignals.h; path = include/lldb/Target/UnixSignals.h; sourceTree = ""; }; + 4C00987011500B4300F316B0 /* UnixSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnixSignals.cpp; path = source/Target/UnixSignals.cpp; sourceTree = ""; }; + 4C09CB73116BD98B00C7A725 /* CommandCompletions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandCompletions.h; path = include/lldb/Interpreter/CommandCompletions.h; sourceTree = ""; }; + 4C09CB74116BD98B00C7A725 /* CommandCompletions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandCompletions.cpp; path = source/Interpreter/CommandCompletions.cpp; sourceTree = ""; }; + 4C43DEF9110641F300E55CBF /* ThreadPlanShouldStopHere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanShouldStopHere.h; path = include/lldb/Target/ThreadPlanShouldStopHere.h; sourceTree = ""; }; + 4C43DEFA110641F300E55CBF /* ThreadPlanShouldStopHere.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanShouldStopHere.cpp; path = source/Target/ThreadPlanShouldStopHere.cpp; sourceTree = ""; }; + 4C43DF8511069BFD00E55CBF /* ThreadPlanStepInRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepInRange.h; path = include/lldb/Target/ThreadPlanStepInRange.h; sourceTree = ""; }; + 4C43DF8611069BFD00E55CBF /* ThreadPlanStepOverRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepOverRange.h; path = include/lldb/Target/ThreadPlanStepOverRange.h; sourceTree = ""; }; + 4C43DF8911069C3200E55CBF /* ThreadPlanStepInRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepInRange.cpp; path = source/Target/ThreadPlanStepInRange.cpp; sourceTree = ""; }; + 4C43DF8A11069C3200E55CBF /* ThreadPlanStepOverRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepOverRange.cpp; path = source/Target/ThreadPlanStepOverRange.cpp; sourceTree = ""; }; + 4C51FF1511A4C485007C962F /* ObjCTrampolineHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjCTrampolineHandler.h; sourceTree = ""; }; + 4C51FF1611A4C486007C962F /* ObjCTrampolineHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjCTrampolineHandler.cpp; sourceTree = ""; }; + 4C98D3DA118FB96F00E575D0 /* ClangFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangFunction.cpp; path = source/Expression/ClangFunction.cpp; sourceTree = ""; }; + 4C98D3DB118FB96F00E575D0 /* RecordingMemoryManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecordingMemoryManager.cpp; path = source/Expression/RecordingMemoryManager.cpp; sourceTree = ""; }; + 4C98D3E0118FB98F00E575D0 /* ClangFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangFunction.h; path = include/lldb/Expression/ClangFunction.h; sourceTree = ""; }; + 4C98D3E1118FB98F00E575D0 /* RecordingMemoryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecordingMemoryManager.h; path = include/lldb/Expression/RecordingMemoryManager.h; sourceTree = ""; }; + 4C98D3E4118FB9B100E575D0 /* CommandObjectCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectCall.cpp; path = source/Commands/CommandObjectCall.cpp; sourceTree = ""; }; + 4C98D3E5118FB9B100E575D0 /* CommandObjectCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectCall.h; path = source/Commands/CommandObjectCall.h; sourceTree = ""; }; + 4CA9637911B6E99A00780E28 /* CommandObjectApropos.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectApropos.cpp; path = source/Commands/CommandObjectApropos.cpp; sourceTree = ""; }; + 4CA9637A11B6E99A00780E28 /* CommandObjectApropos.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectApropos.h; path = source/Commands/CommandObjectApropos.h; sourceTree = ""; }; + 4CAFCE001101216B00CA63DB /* ThreadPlanRunToAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanRunToAddress.h; path = include/lldb/Target/ThreadPlanRunToAddress.h; sourceTree = ""; }; + 4CAFCE031101218900CA63DB /* ThreadPlanRunToAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanRunToAddress.cpp; path = source/Target/ThreadPlanRunToAddress.cpp; sourceTree = ""; }; + 4CEDAED311754F5E00E875A6 /* ThreadPlanStepUntil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanStepUntil.h; path = include/lldb/Target/ThreadPlanStepUntil.h; sourceTree = ""; }; + 4CEE62FA1145F2130064CF93 /* ProcessGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessGDBRemote.cpp; path = "source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp"; sourceTree = ""; }; + 4CEE62FB1145F2130064CF93 /* ProcessGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProcessGDBRemote.h; path = "source/Plugins/Process/gdb-remote/ProcessGDBRemote.h"; sourceTree = ""; }; + 4CEE62FC1145F2130064CF93 /* ProcessGDBRemoteLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProcessGDBRemoteLog.cpp; path = "source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp"; sourceTree = ""; }; + 4CEE62FD1145F2130064CF93 /* ProcessGDBRemoteLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProcessGDBRemoteLog.h; path = "source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"; sourceTree = ""; }; + 4CEE62FE1145F2130064CF93 /* ThreadGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadGDBRemote.cpp; path = "source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp"; sourceTree = ""; }; + 4CEE62FF1145F2130064CF93 /* ThreadGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadGDBRemote.h; path = "source/Plugins/Process/gdb-remote/ThreadGDBRemote.h"; sourceTree = ""; }; + 4CF4473D11A8687100EF971E /* ThreadPlanStepThroughObjCTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadPlanStepThroughObjCTrampoline.h; sourceTree = ""; }; + 4CF4473E11A8687100EF971E /* ThreadPlanStepThroughObjCTrampoline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadPlanStepThroughObjCTrampoline.cpp; sourceTree = ""; }; + 9654F79C1197DA1300F72B43 /* MacOSXLibunwindCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MacOSXLibunwindCallbacks.cpp; path = Utility/MacOSXLibunwindCallbacks.cpp; sourceTree = ""; }; + 9654F79D1197DA1300F72B43 /* MacOSXLibunwindCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MacOSXLibunwindCallbacks.h; path = Utility/MacOSXLibunwindCallbacks.h; sourceTree = ""; }; + 9654F7A11197DA3F00F72B43 /* libunwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libunwind.h; sourceTree = ""; }; + 9654F7A31197DA3F00F72B43 /* compact_unwind_encoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind_encoding.h; sourceTree = ""; }; + 9654F7A41197DA3F00F72B43 /* unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unwind.h; sourceTree = ""; }; + 9654F7A61197DA3F00F72B43 /* AddressSpace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddressSpace.hpp; sourceTree = ""; }; + 9654F7A71197DA3F00F72B43 /* ArchDefaultUnwinder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ArchDefaultUnwinder.hpp; sourceTree = ""; }; + 9654F7A81197DA3F00F72B43 /* AssemblyInstructions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AssemblyInstructions.hpp; sourceTree = ""; }; + 9654F7A91197DA3F00F72B43 /* AssemblyParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AssemblyParser.hpp; sourceTree = ""; }; + 9654F7AA1197DA3F00F72B43 /* CompactUnwinder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CompactUnwinder.hpp; sourceTree = ""; }; + 9654F7AB1197DA3F00F72B43 /* dwarf2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarf2.h; sourceTree = ""; }; + 9654F7AC1197DA3F00F72B43 /* DwarfInstructions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfInstructions.hpp; sourceTree = ""; }; + 9654F7AD1197DA3F00F72B43 /* DwarfParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfParser.hpp; sourceTree = ""; }; + 9654F7AE1197DA3F00F72B43 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = ""; }; + 9654F7AF1197DA3F00F72B43 /* InternalMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalMacros.h; sourceTree = ""; }; + 9654F7B21197DA3F00F72B43 /* libunwind_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libunwind_priv.h; sourceTree = ""; }; + 9654F7B31197DA3F00F72B43 /* libuwind.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libuwind.cxx; sourceTree = ""; }; + 9654F7B41197DA3F00F72B43 /* Registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Registers.hpp; sourceTree = ""; }; + 9654F7B51197DA3F00F72B43 /* Registers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = Registers.s; sourceTree = ""; }; + 9654F7B61197DA3F00F72B43 /* RemoteDebuggerDummyUnwinder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RemoteDebuggerDummyUnwinder.hpp; sourceTree = ""; }; + 9654F7B71197DA3F00F72B43 /* RemoteProcInfo.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RemoteProcInfo.hpp; sourceTree = ""; }; + 9654F7B81197DA3F00F72B43 /* RemoteRegisterMap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RemoteRegisterMap.hpp; sourceTree = ""; }; + 9654F7B91197DA3F00F72B43 /* RemoteUnwindProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteUnwindProfile.h; sourceTree = ""; }; + 9654F7BA1197DA3F00F72B43 /* unw_getcontext.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = unw_getcontext.s; sourceTree = ""; }; + 9654F7BD1197DA3F00F72B43 /* UnwindCursor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UnwindCursor.hpp; sourceTree = ""; }; + 9A19A6A51163BB7E00E0D453 /* SBValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBValue.h; path = include/lldb/API/SBValue.h; sourceTree = ""; }; + 9A19A6AD1163BB9800E0D453 /* SBValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBValue.cpp; path = source/API/SBValue.cpp; sourceTree = ""; }; + 9A2771FB1135A35C00E6ADB6 /* ScriptInterpreterNone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScriptInterpreterNone.h; path = include/lldb/Interpreter/ScriptInterpreterNone.h; sourceTree = ""; }; + 9A2771FC1135A37500E6ADB6 /* ScriptInterpreterNone.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScriptInterpreterNone.cpp; path = source/Interpreter/ScriptInterpreterNone.cpp; sourceTree = ""; }; + 9A357582116CFDEE00E8ED2F /* SBValueList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBValueList.h; path = include/lldb/API/SBValueList.h; sourceTree = ""; }; + 9A35758D116CFE0F00E8ED2F /* SBValueList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBValueList.cpp; path = source/API/SBValueList.cpp; sourceTree = ""; }; + 9A35765E116E76A700E8ED2F /* StringList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringList.h; path = include/lldb/Core/StringList.h; sourceTree = ""; }; + 9A35765F116E76B900E8ED2F /* StringList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringList.cpp; path = source/Core/StringList.cpp; sourceTree = ""; }; + 9A357670116E7B5200E8ED2F /* SBStringList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBStringList.h; path = include/lldb/API/SBStringList.h; sourceTree = ""; }; + 9A357672116E7B6400E8ED2F /* SBStringList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBStringList.cpp; path = source/API/SBStringList.cpp; sourceTree = ""; }; + 9A3576A7116E9AB700E8ED2F /* SBHostOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBHostOS.h; path = include/lldb/API/SBHostOS.h; sourceTree = ""; }; + 9A3576A9116E9AC700E8ED2F /* SBHostOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBHostOS.cpp; path = source/API/SBHostOS.cpp; sourceTree = ""; }; + 9A42976111861A9F00FE05CD /* CommandObjectBreakpointCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectBreakpointCommand.h; path = source/Commands/CommandObjectBreakpointCommand.h; sourceTree = ""; }; + 9A42976211861AA600FE05CD /* CommandObjectBreakpointCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectBreakpointCommand.cpp; path = source/Commands/CommandObjectBreakpointCommand.cpp; sourceTree = ""; }; + 9A633FE7112DCE3C001A7E43 /* SBFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBFrame.cpp; path = source/API/SBFrame.cpp; sourceTree = ""; }; + 9A633FE8112DCE3C001A7E43 /* SBFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBFrame.h; path = include/lldb/API/SBFrame.h; sourceTree = ""; }; + 9A82010B10FFB49800182560 /* ScriptInterpreter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScriptInterpreter.cpp; path = source/Interpreter/ScriptInterpreter.cpp; sourceTree = ""; }; + 9A8B4EA210FD515000C68FF2 /* CommandObjectUnalias.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommandObjectUnalias.h; path = source/Commands/CommandObjectUnalias.h; sourceTree = ""; }; + 9A8B4EA310FD516400C68FF2 /* CommandObjectUnalias.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandObjectUnalias.cpp; path = source/Commands/CommandObjectUnalias.cpp; sourceTree = ""; }; + 9A9830F21125FC5800A56CB0 /* SBBroadcaster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBroadcaster.cpp; path = source/API/SBBroadcaster.cpp; sourceTree = ""; }; + 9A9830F31125FC5800A56CB0 /* SBBroadcaster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBBroadcaster.h; path = include/lldb/API/SBBroadcaster.h; sourceTree = ""; }; + 9A9830F41125FC5800A56CB0 /* SBCommandContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBCommandContext.cpp; path = source/API/SBCommandContext.cpp; sourceTree = ""; }; + 9A9830F51125FC5800A56CB0 /* SBCommandContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBCommandContext.h; path = include/lldb/API/SBCommandContext.h; sourceTree = ""; }; + 9A9830F61125FC5800A56CB0 /* SBCommandInterpreter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBCommandInterpreter.cpp; path = source/API/SBCommandInterpreter.cpp; sourceTree = ""; }; + 9A9830F71125FC5800A56CB0 /* SBCommandInterpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBCommandInterpreter.h; path = include/lldb/API/SBCommandInterpreter.h; sourceTree = ""; }; + 9A9830F81125FC5800A56CB0 /* SBCommandReturnObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBCommandReturnObject.cpp; path = source/API/SBCommandReturnObject.cpp; sourceTree = ""; }; + 9A9830F91125FC5800A56CB0 /* SBCommandReturnObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBCommandReturnObject.h; path = include/lldb/API/SBCommandReturnObject.h; sourceTree = ""; }; + 9A9830FA1125FC5800A56CB0 /* SBDebugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBDebugger.cpp; path = source/API/SBDebugger.cpp; sourceTree = ""; }; + 9A9830FB1125FC5800A56CB0 /* SBDebugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBDebugger.h; path = include/lldb/API/SBDebugger.h; sourceTree = ""; }; + 9A9830FC1125FC5800A56CB0 /* SBDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBDefines.h; path = include/lldb/API/SBDefines.h; sourceTree = ""; }; + 9A9830FD1125FC5800A56CB0 /* SBEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBEvent.cpp; path = source/API/SBEvent.cpp; sourceTree = ""; }; + 9A9830FE1125FC5800A56CB0 /* SBEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBEvent.h; path = include/lldb/API/SBEvent.h; sourceTree = ""; }; + 9A9831011125FC5800A56CB0 /* SBListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBListener.cpp; path = source/API/SBListener.cpp; sourceTree = ""; }; + 9A9831021125FC5800A56CB0 /* SBListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBListener.h; path = include/lldb/API/SBListener.h; sourceTree = ""; }; + 9A9831031125FC5800A56CB0 /* SBProcess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBProcess.cpp; path = source/API/SBProcess.cpp; sourceTree = ""; }; + 9A9831041125FC5800A56CB0 /* SBProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBProcess.h; path = include/lldb/API/SBProcess.h; sourceTree = ""; }; + 9A9831051125FC5800A56CB0 /* SBSourceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBSourceManager.cpp; path = source/API/SBSourceManager.cpp; sourceTree = ""; }; + 9A9831061125FC5800A56CB0 /* SBSourceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBSourceManager.h; path = include/lldb/API/SBSourceManager.h; sourceTree = ""; }; + 9A9831071125FC5800A56CB0 /* SBTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBTarget.cpp; path = source/API/SBTarget.cpp; sourceTree = ""; }; + 9A9831081125FC5800A56CB0 /* SBTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBTarget.h; path = include/lldb/API/SBTarget.h; sourceTree = ""; }; + 9A9831091125FC5800A56CB0 /* SBThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBThread.cpp; path = source/API/SBThread.cpp; sourceTree = ""; }; + 9A98310A1125FC5800A56CB0 /* SBThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBThread.h; path = include/lldb/API/SBThread.h; sourceTree = ""; }; + 9AA69DAE118A023300D753A0 /* SBInputReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBInputReader.h; path = include/lldb/API/SBInputReader.h; sourceTree = ""; }; + 9AA69DB0118A024600D753A0 /* SBInputReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBInputReader.cpp; path = source/API/SBInputReader.cpp; sourceTree = ""; }; + 9AA69DB5118A027A00D753A0 /* InputReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InputReader.cpp; path = source/Core/InputReader.cpp; sourceTree = ""; }; + 9AA69DBB118A029E00D753A0 /* InputReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InputReader.h; path = include/lldb/Core/InputReader.h; sourceTree = ""; }; + 9AC7033D11752C4C0086C050 /* AddressResolverFileLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddressResolverFileLine.h; path = include/lldb/Core/AddressResolverFileLine.h; sourceTree = ""; }; + 9AC7033E11752C540086C050 /* AddressResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddressResolver.h; path = include/lldb/Core/AddressResolver.h; sourceTree = ""; }; + 9AC7033F11752C590086C050 /* AddressResolverName.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddressResolverName.h; path = include/lldb/Core/AddressResolverName.h; sourceTree = ""; }; + 9AC7034011752C6B0086C050 /* AddressResolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddressResolver.cpp; path = source/Core/AddressResolver.cpp; sourceTree = ""; }; + 9AC7034211752C720086C050 /* AddressResolverFileLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddressResolverFileLine.cpp; path = source/Core/AddressResolverFileLine.cpp; sourceTree = ""; }; + 9AC7034411752C790086C050 /* AddressResolverName.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddressResolverName.cpp; path = source/Core/AddressResolverName.cpp; sourceTree = ""; }; + 9AC7038D117674EB0086C050 /* SBInstruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBInstruction.h; path = include/lldb/API/SBInstruction.h; sourceTree = ""; }; + 9AC7038F117675270086C050 /* SBInstructionList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBInstructionList.h; path = include/lldb/API/SBInstructionList.h; sourceTree = ""; }; + 9AC703AE117675410086C050 /* SBInstruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBInstruction.cpp; path = source/API/SBInstruction.cpp; sourceTree = ""; }; + 9AC703B0117675490086C050 /* SBInstructionList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBInstructionList.cpp; path = source/API/SBInstructionList.cpp; sourceTree = ""; }; + 9AF16A9C11402D5B007A7B3F /* SBBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBreakpoint.cpp; path = source/API/SBBreakpoint.cpp; sourceTree = ""; }; + 9AF16A9E11402D69007A7B3F /* SBBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBBreakpoint.h; path = include/lldb/API/SBBreakpoint.h; sourceTree = ""; }; + 9AF16CC611408686007A7B3F /* SBBreakpointLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SBBreakpointLocation.h; path = include/lldb/API/SBBreakpointLocation.h; sourceTree = ""; }; + 9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBreakpointLocation.cpp; path = source/API/SBBreakpointLocation.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 26680205115FD0ED008E1FE4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2668022F115FD19D008E1FE4 /* CoreFoundation.framework in Frameworks */, + 26680230115FD19E008E1FE4 /* DebugSymbols.framework in Frameworks */, + 26680231115FD1A0008E1FE4 /* Foundation.framework in Frameworks */, + 26680232115FD1A4008E1FE4 /* libpython2.6.dylib in Frameworks */, + 26680233115FD1A7008E1FE4 /* libobjc.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 26F5C26810F3D9A4009D5894 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 26F5C32510F3DF23009D5894 /* libpython2.6.dylib in Frameworks */, + 26F5C32C10F3DFDD009D5894 /* libedit.dylib in Frameworks */, + 26F5C32D10F3DFDD009D5894 /* libtermcap.dylib in Frameworks */, + 26F5C37510F3F61B009D5894 /* libobjc.dylib in Frameworks */, + 26F5C39110F3FA26009D5894 /* CoreFoundation.framework in Frameworks */, + 265ABF6310F42EE900531910 /* DebugSymbols.framework in Frameworks */, + 260C876A10F538E700BB2B04 /* Foundation.framework in Frameworks */, + 2668035C11601108008E1FE4 /* LLDB.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* lldb */ = { + isa = PBXGroup; + children = ( + 26C9DF02113C5B80006B0F94 /* Include */, + 26F5C32810F3DF7D009D5894 /* Libraries */, + 266960581199F4230075C61A /* Scripts */, + 08FB7795FE84155DC02AAC07 /* Source */, + 26F5C22410F3D950009D5894 /* Tools */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = lldb; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 26BC7E7410F1B85900F91463 /* lldb.cpp */, + 26BC7E7510F1B85900F91463 /* lldb-log.cpp */, + 26BC7C2A10F1B3BC00F91463 /* lldb-private.h */, + 26BC7C2810F1B3BC00F91463 /* lldb-private-interfaces.h */, + 26BC7D5D10F1B77400F91463 /* lldb-private-log.h */, + 262D3190111B4341004E6F88 /* API */, + 26BC7CEB10F1B70800F91463 /* Breakpoint */, + 26BC7D0D10F1B71D00F91463 /* Commands */, + 26BC7C1010F1B34800F91463 /* Core */, + 26BC7DBE10F1B78200F91463 /* Expression */, + 26BC7DD010F1B7C100F91463 /* Host */, + 26BC7DDF10F1B7E200F91463 /* Interpreter */, + 260C897110F57C5600BB2B04 /* Plugins */, + 26BC7C4B10F1B6C100F91463 /* Symbol */, + 26BC7DEF10F1B80200F91463 /* Target */, + 2682F168115ED9C800CCFF99 /* Utility */, + ); + name = Source; + sourceTree = ""; + usesTabs = 0; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 26F5C26A10F3D9A4009D5894 /* lldb */, + 26680207115FD0ED008E1FE4 /* LLDB.framework */, + ); + name = Products; + sourceTree = ""; + usesTabs = 0; + }; + 260C897110F57C5600BB2B04 /* Plugins */ = { + isa = PBXGroup; + children = ( + 493C63D711891A8000914D5E /* ABI */, + 260C897210F57C5600BB2B04 /* Disassembler */, + 260C897810F57C5600BB2B04 /* DynamicLoader */, + 260C897E10F57C5600BB2B04 /* ObjectContainer */, + 260C898210F57C5600BB2B04 /* ObjectFile */, + 260C898A10F57C5600BB2B04 /* Process */, + 260C89B110F57C5600BB2B04 /* SymbolFile */, + 260C89E010F57C5600BB2B04 /* SymbolVendor */, + ); + name = Plugins; + path = source/Plugins; + sourceTree = ""; + }; + 260C897210F57C5600BB2B04 /* Disassembler */ = { + isa = PBXGroup; + children = ( + 260C897310F57C5600BB2B04 /* llvm */, + ); + path = Disassembler; + sourceTree = ""; + }; + 260C897310F57C5600BB2B04 /* llvm */ = { + isa = PBXGroup; + children = ( + 260C897410F57C5600BB2B04 /* DisassemblerLLVM.cpp */, + 260C897510F57C5600BB2B04 /* DisassemblerLLVM.h */, + ); + path = llvm; + sourceTree = ""; + }; + 260C897810F57C5600BB2B04 /* DynamicLoader */ = { + isa = PBXGroup; + children = ( + 260C897910F57C5600BB2B04 /* MacOSX-DYLD */, + ); + path = DynamicLoader; + sourceTree = ""; + }; + 260C897910F57C5600BB2B04 /* MacOSX-DYLD */ = { + isa = PBXGroup; + children = ( + 260C897A10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.cpp */, + 260C897B10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLD.h */, + 260C897C10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLDLog.cpp */, + 260C897D10F57C5600BB2B04 /* DynamicLoaderMacOSXDYLDLog.h */, + 4C51FF1511A4C485007C962F /* ObjCTrampolineHandler.h */, + 4C51FF1611A4C486007C962F /* ObjCTrampolineHandler.cpp */, + 4CF4473D11A8687100EF971E /* ThreadPlanStepThroughObjCTrampoline.h */, + 4CF4473E11A8687100EF971E /* ThreadPlanStepThroughObjCTrampoline.cpp */, + ); + path = "MacOSX-DYLD"; + sourceTree = ""; + }; + 260C897E10F57C5600BB2B04 /* ObjectContainer */ = { + isa = PBXGroup; + children = ( + 26A3B4AB1181454800381BC2 /* BSD-Archive */, + 260C897F10F57C5600BB2B04 /* Universal-Mach-O */, + ); + path = ObjectContainer; + sourceTree = ""; + }; + 260C897F10F57C5600BB2B04 /* Universal-Mach-O */ = { + isa = PBXGroup; + children = ( + 260C898010F57C5600BB2B04 /* ObjectContainerUniversalMachO.cpp */, + 260C898110F57C5600BB2B04 /* ObjectContainerUniversalMachO.h */, + ); + path = "Universal-Mach-O"; + sourceTree = ""; + }; + 260C898210F57C5600BB2B04 /* ObjectFile */ = { + isa = PBXGroup; + children = ( + 260C898310F57C5600BB2B04 /* ELF */, + 260C898710F57C5600BB2B04 /* Mach-O */, + ); + path = ObjectFile; + sourceTree = ""; + }; + 260C898310F57C5600BB2B04 /* ELF */ = { + isa = PBXGroup; + children = ( + 260C898410F57C5600BB2B04 /* elf.h */, + 260C898510F57C5600BB2B04 /* ObjectFileELF.cpp */, + 260C898610F57C5600BB2B04 /* ObjectFileELF.h */, + ); + path = ELF; + sourceTree = ""; + }; + 260C898710F57C5600BB2B04 /* Mach-O */ = { + isa = PBXGroup; + children = ( + 260C898810F57C5600BB2B04 /* ObjectFileMachO.cpp */, + 260C898910F57C5600BB2B04 /* ObjectFileMachO.h */, + ); + path = "Mach-O"; + sourceTree = ""; + }; + 260C898A10F57C5600BB2B04 /* Process */ = { + isa = PBXGroup; + children = ( + 4CEE62F71145F1C70064CF93 /* GDB Remote */, + 260C898B10F57C5600BB2B04 /* MacOSX-User */, + 26B4666E11A2080F00CF6220 /* Utility */, + ); + path = Process; + sourceTree = ""; + }; + 260C898B10F57C5600BB2B04 /* MacOSX-User */ = { + isa = PBXGroup; + children = ( + 260C898C10F57C5600BB2B04 /* scripts */, + 260C899010F57C5600BB2B04 /* source */, + ); + path = "MacOSX-User"; + sourceTree = ""; + }; + 260C898C10F57C5600BB2B04 /* scripts */ = { + isa = PBXGroup; + children = ( + 260C898D10F57C5600BB2B04 /* cc-swig */, + 260C898E10F57C5600BB2B04 /* config.pl */, + 260C898F10F57C5600BB2B04 /* test-ProcessDebug.pl */, + ); + path = scripts; + sourceTree = ""; + }; + 260C899010F57C5600BB2B04 /* source */ = { + isa = PBXGroup; + children = ( + 260C899110F57C5600BB2B04 /* MacOSX */, + 260C89A310F57C5600BB2B04 /* ProcessMacOSX.cpp */, + 260C89A410F57C5600BB2B04 /* ProcessMacOSX.h */, + 260C89A510F57C5600BB2B04 /* ProcessMacOSXLog.cpp */, + 260C89A610F57C5600BB2B04 /* ProcessMacOSXLog.h */, + 260C89A910F57C5600BB2B04 /* RegisterContextMach_arm.cpp */, + 260C89AA10F57C5600BB2B04 /* RegisterContextMach_arm.h */, + 260C89AB10F57C5600BB2B04 /* RegisterContextMach_i386.cpp */, + 260C89AC10F57C5600BB2B04 /* RegisterContextMach_i386.h */, + 260C89AD10F57C5600BB2B04 /* RegisterContextMach_x86_64.cpp */, + 260C89AE10F57C5600BB2B04 /* RegisterContextMach_x86_64.h */, + 260C89AF10F57C5600BB2B04 /* ThreadMacOSX.cpp */, + 260C89B010F57C5600BB2B04 /* ThreadMacOSX.h */, + ); + path = source; + sourceTree = ""; + }; + 260C899110F57C5600BB2B04 /* MacOSX */ = { + isa = PBXGroup; + children = ( + 260C899210F57C5600BB2B04 /* MachException.cpp */, + 260C899310F57C5600BB2B04 /* MachException.h */, + 260C899410F57C5600BB2B04 /* MachTask.cpp */, + 260C899510F57C5600BB2B04 /* MachTask.h */, + 260C899610F57C5600BB2B04 /* MachThreadContext.h */, + 260C899710F57C5600BB2B04 /* MachThreadContext_arm.cpp */, + 260C899810F57C5600BB2B04 /* MachThreadContext_arm.h */, + 260C899910F57C5600BB2B04 /* MachThreadContext_i386.cpp */, + 260C899A10F57C5600BB2B04 /* MachThreadContext_i386.h */, + 260C899B10F57C5600BB2B04 /* MachThreadContext_x86_64.cpp */, + 260C899C10F57C5600BB2B04 /* MachThreadContext_x86_64.h */, + 260C899D10F57C5600BB2B04 /* MachVMMemory.cpp */, + 260C899E10F57C5600BB2B04 /* MachVMMemory.h */, + 260C899F10F57C5600BB2B04 /* MachVMRegion.cpp */, + 260C89A010F57C5600BB2B04 /* MachVMRegion.h */, + 260C89A110F57C5600BB2B04 /* ProcessControl-mig.defs */, + ); + path = MacOSX; + sourceTree = ""; + }; + 260C89B110F57C5600BB2B04 /* SymbolFile */ = { + isa = PBXGroup; + children = ( + 260C89B210F57C5600BB2B04 /* DWARF */, + 260C89DD10F57C5600BB2B04 /* Symtab */, + ); + path = SymbolFile; + sourceTree = ""; + }; + 260C89B210F57C5600BB2B04 /* DWARF */ = { + isa = PBXGroup; + children = ( + 260C89B310F57C5600BB2B04 /* DWARFAbbreviationDeclaration.cpp */, + 260C89B410F57C5600BB2B04 /* DWARFAbbreviationDeclaration.h */, + 260C89B610F57C5600BB2B04 /* DWARFAttribute.h */, + 260C89B710F57C5600BB2B04 /* DWARFCompileUnit.cpp */, + 260C89B810F57C5600BB2B04 /* DWARFCompileUnit.h */, + 260C89B910F57C5600BB2B04 /* DWARFDebugAbbrev.cpp */, + 260C89BA10F57C5600BB2B04 /* DWARFDebugAbbrev.h */, + 260C89BB10F57C5600BB2B04 /* DWARFDebugAranges.cpp */, + 260C89BC10F57C5600BB2B04 /* DWARFDebugAranges.h */, + 260C89BD10F57C5600BB2B04 /* DWARFDebugArangeSet.cpp */, + 260C89BE10F57C5600BB2B04 /* DWARFDebugArangeSet.h */, + 260C89BF10F57C5600BB2B04 /* DWARFDebugInfo.cpp */, + 260C89C010F57C5600BB2B04 /* DWARFDebugInfo.h */, + 260C89C110F57C5600BB2B04 /* DWARFDebugInfoEntry.cpp */, + 260C89C210F57C5600BB2B04 /* DWARFDebugInfoEntry.h */, + 260C89C310F57C5600BB2B04 /* DWARFDebugLine.cpp */, + 260C89C410F57C5600BB2B04 /* DWARFDebugLine.h */, + 260C89C510F57C5600BB2B04 /* DWARFDebugMacinfo.cpp */, + 260C89C610F57C5600BB2B04 /* DWARFDebugMacinfo.h */, + 260C89C710F57C5600BB2B04 /* DWARFDebugMacinfoEntry.cpp */, + 260C89C810F57C5600BB2B04 /* DWARFDebugMacinfoEntry.h */, + 260C89C910F57C5600BB2B04 /* DWARFDebugPubnames.cpp */, + 260C89CA10F57C5600BB2B04 /* DWARFDebugPubnames.h */, + 260C89CB10F57C5600BB2B04 /* DWARFDebugPubnamesSet.cpp */, + 260C89CC10F57C5600BB2B04 /* DWARFDebugPubnamesSet.h */, + 260C89CD10F57C5600BB2B04 /* DWARFDebugRanges.cpp */, + 260C89CE10F57C5600BB2B04 /* DWARFDebugRanges.h */, + 260C89CF10F57C5600BB2B04 /* DWARFDefines.c */, + 260C89D010F57C5600BB2B04 /* DWARFDefines.h */, + 260C89D110F57C5600BB2B04 /* DWARFDIECollection.cpp */, + 260C89D210F57C5600BB2B04 /* DWARFDIECollection.h */, + 260C89D310F57C5600BB2B04 /* DWARFFormValue.cpp */, + 260C89D410F57C5600BB2B04 /* DWARFFormValue.h */, + 260C89D510F57C5600BB2B04 /* DWARFLocationDescription.cpp */, + 260C89D610F57C5600BB2B04 /* DWARFLocationDescription.h */, + 260C89D710F57C5600BB2B04 /* DWARFLocationList.cpp */, + 260C89D810F57C5600BB2B04 /* DWARFLocationList.h */, + 260C89D910F57C5600BB2B04 /* SymbolFileDWARF.cpp */, + 260C89DA10F57C5600BB2B04 /* SymbolFileDWARF.h */, + 26109B3B1155D70100CC3529 /* LogChannelDWARF.cpp */, + 26109B3C1155D70100CC3529 /* LogChannelDWARF.h */, + 260C89DB10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.cpp */, + 260C89DC10F57C5600BB2B04 /* SymbolFileDWARFDebugMap.h */, + ); + path = DWARF; + sourceTree = ""; + }; + 260C89DD10F57C5600BB2B04 /* Symtab */ = { + isa = PBXGroup; + children = ( + 260C89DE10F57C5600BB2B04 /* SymbolFileSymtab.cpp */, + 260C89DF10F57C5600BB2B04 /* SymbolFileSymtab.h */, + ); + path = Symtab; + sourceTree = ""; + }; + 260C89E010F57C5600BB2B04 /* SymbolVendor */ = { + isa = PBXGroup; + children = ( + 260C89E110F57C5600BB2B04 /* MacOSX */, + ); + path = SymbolVendor; + sourceTree = ""; + }; + 260C89E110F57C5600BB2B04 /* MacOSX */ = { + isa = PBXGroup; + children = ( + 260C89E210F57C5600BB2B04 /* SymbolVendorMacOSX.cpp */, + 260C89E310F57C5600BB2B04 /* SymbolVendorMacOSX.h */, + ); + path = MacOSX; + sourceTree = ""; + }; + 262D3190111B4341004E6F88 /* API */ = { + isa = PBXGroup; + children = ( + 26BC7C2510F1B3BC00F91463 /* lldb-defines.h */, + 26BC7C2610F1B3BC00F91463 /* lldb-enumerations.h */, + 26DE1E6911616C2E00A093E2 /* lldb-forward-rtti.h */, + 26DE1E6A11616C2E00A093E2 /* lldb-forward.h */, + 26B42B1E1187A92B0079C8C8 /* lldb-include.h */, + 26BC7C2910F1B3BC00F91463 /* lldb-types.h */, + 26B42C4C1187ABA50079C8C8 /* LLDB.h */, + 9A9830FC1125FC5800A56CB0 /* SBDefines.h */, + 26DE204211618ACA00A093E2 /* SBAddress.h */, + 26DE204411618ADA00A093E2 /* SBAddress.cpp */, + 26DE205611618FC500A093E2 /* SBBlock.h */, + 26DE20601161902600A093E2 /* SBBlock.cpp */, + 9AF16A9E11402D69007A7B3F /* SBBreakpoint.h */, + 9AF16A9C11402D5B007A7B3F /* SBBreakpoint.cpp */, + 9AF16CC611408686007A7B3F /* SBBreakpointLocation.h */, + 9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */, + 9A9830F31125FC5800A56CB0 /* SBBroadcaster.h */, + 9A9830F21125FC5800A56CB0 /* SBBroadcaster.cpp */, + 9A9830F51125FC5800A56CB0 /* SBCommandContext.h */, + 9A9830F41125FC5800A56CB0 /* SBCommandContext.cpp */, + 9A9830F71125FC5800A56CB0 /* SBCommandInterpreter.h */, + 9A9830F61125FC5800A56CB0 /* SBCommandInterpreter.cpp */, + 9A9830F91125FC5800A56CB0 /* SBCommandReturnObject.h */, + 9A9830F81125FC5800A56CB0 /* SBCommandReturnObject.cpp */, + 260223E7115F06D500A601A2 /* SBCommunication.h */, + 260223E8115F06E500A601A2 /* SBCommunication.cpp */, + 26DE205411618FB800A093E2 /* SBCompileUnit.h */, + 26DE205E1161901B00A093E2 /* SBCompileUnit.cpp */, + 9A9830FB1125FC5800A56CB0 /* SBDebugger.h */, + 9A9830FA1125FC5800A56CB0 /* SBDebugger.cpp */, + 2682F286115EF3BD00CCFF99 /* SBError.h */, + 2682F284115EF3A700CCFF99 /* SBError.cpp */, + 9A9830FE1125FC5800A56CB0 /* SBEvent.h */, + 9A9830FD1125FC5800A56CB0 /* SBEvent.cpp */, + 26022531115F27FA00A601A2 /* SBFileSpec.h */, + 26022532115F281400A601A2 /* SBFileSpec.cpp */, + 9A633FE8112DCE3C001A7E43 /* SBFrame.h */, + 9A633FE7112DCE3C001A7E43 /* SBFrame.cpp */, + 26DE205211618FAC00A093E2 /* SBFunction.h */, + 26DE205C1161901400A093E2 /* SBFunction.cpp */, + 9A3576A7116E9AB700E8ED2F /* SBHostOS.h */, + 9A3576A9116E9AC700E8ED2F /* SBHostOS.cpp */, + 9AA69DAE118A023300D753A0 /* SBInputReader.h */, + 9AA69DB0118A024600D753A0 /* SBInputReader.cpp */, + 9AC7038D117674EB0086C050 /* SBInstruction.h */, + 9AC703AE117675410086C050 /* SBInstruction.cpp */, + 9AC7038F117675270086C050 /* SBInstructionList.h */, + 9AC703B0117675490086C050 /* SBInstructionList.cpp */, + 26DE205811618FE700A093E2 /* SBLineEntry.h */, + 26DE20621161904200A093E2 /* SBLineEntry.cpp */, + 9A9831021125FC5800A56CB0 /* SBListener.h */, + 9A9831011125FC5800A56CB0 /* SBListener.cpp */, + 26DE204E11618E9800A093E2 /* SBModule.h */, + 26DE204C11618E7A00A093E2 /* SBModule.cpp */, + 9A9831041125FC5800A56CB0 /* SBProcess.h */, + 9A9831031125FC5800A56CB0 /* SBProcess.cpp */, + 9A9831061125FC5800A56CB0 /* SBSourceManager.h */, + 9A9831051125FC5800A56CB0 /* SBSourceManager.cpp */, + 9A357670116E7B5200E8ED2F /* SBStringList.h */, + 9A357672116E7B6400E8ED2F /* SBStringList.cpp */, + 26DE205A11618FF600A093E2 /* SBSymbol.h */, + 26DE20641161904E00A093E2 /* SBSymbol.cpp */, + 26DE204011618AB900A093E2 /* SBSymbolContext.h */, + 26DE204611618AED00A093E2 /* SBSymbolContext.cpp */, + 9A9831081125FC5800A56CB0 /* SBTarget.h */, + 9A9831071125FC5800A56CB0 /* SBTarget.cpp */, + 9A98310A1125FC5800A56CB0 /* SBThread.h */, + 9A9831091125FC5800A56CB0 /* SBThread.cpp */, + 2617447911685869005ADD65 /* SBType.h */, + 261744771168585B005ADD65 /* SBType.cpp */, + 9A19A6A51163BB7E00E0D453 /* SBValue.h */, + 9A19A6AD1163BB9800E0D453 /* SBValue.cpp */, + 9A357582116CFDEE00E8ED2F /* SBValueList.h */, + 9A35758D116CFE0F00E8ED2F /* SBValueList.cpp */, + ); + name = API; + sourceTree = ""; + }; + 265E9BE0115C2B8500D0DCCB /* debugserver */ = { + isa = PBXGroup; + children = ( + 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */, + ); + name = debugserver; + sourceTree = ""; + }; + 265E9BE2115C2BAA00D0DCCB /* Products */ = { + isa = PBXGroup; + children = ( + 26CE05A0115C31E50022F371 /* debugserver */, + ); + name = Products; + sourceTree = ""; + }; + 266960581199F4230075C61A /* Scripts */ = { + isa = PBXGroup; + children = ( + 266960591199F4230075C61A /* build-llvm.pl */, + 2669605A1199F4230075C61A /* build-swig-wrapper-classes.sh */, + 2669605B1199F4230075C61A /* checkpoint-llvm.pl */, + 2669605C1199F4230075C61A /* finish-swig-wrapper-classes.sh */, + 2669605D1199F4230075C61A /* install-lldb.sh */, + 2669605E1199F4230075C61A /* lldb.swig */, + 2669605F1199F4230075C61A /* Python */, + 266960631199F4230075C61A /* sed-sources */, + ); + name = Scripts; + path = scripts; + sourceTree = ""; + }; + 2669605F1199F4230075C61A /* Python */ = { + isa = PBXGroup; + children = ( + 266960601199F4230075C61A /* build-swig-Python.sh */, + 266960611199F4230075C61A /* edit-swig-python-wrapper-file.py */, + 266960621199F4230075C61A /* finish-swig-Python-lldb.sh */, + ); + path = Python; + sourceTree = ""; + }; + 2682F168115ED9C800CCFF99 /* Utility */ = { + isa = PBXGroup; + children = ( + 26F996A7119B79C300412154 /* ARM_DWARF_Registers.h */, + 26F996A8119B79C300412154 /* ARM_GCC_Registers.h */, + 2660D9F611922A1300958FBD /* StringExtractor.cpp */, + 2660D9F711922A1300958FBD /* StringExtractor.h */, + 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */, + 2676A094119C93C8008A98EF /* StringExtractorGDBRemote.h */, + 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */, + 2682F16B115EDA0D00CCFF99 /* PseudoTerminal.h */, + ); + name = Utility; + sourceTree = ""; + }; + 26A3B4AB1181454800381BC2 /* BSD-Archive */ = { + isa = PBXGroup; + children = ( + 26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */, + 26A3B4AD1181454800381BC2 /* ObjectContainerBSDArchive.h */, + ); + path = "BSD-Archive"; + sourceTree = ""; + }; + 26B4666E11A2080F00CF6220 /* Utility */ = { + isa = PBXGroup; + children = ( + 9654F79F1197DA3F00F72B43 /* libunwind */, + 26B4666F11A2091600CF6220 /* LibUnwindRegisterContext.cpp */, + 26B4667011A2091600CF6220 /* LibUnwindRegisterContext.h */, + 9654F79C1197DA1300F72B43 /* MacOSXLibunwindCallbacks.cpp */, + 9654F79D1197DA1300F72B43 /* MacOSXLibunwindCallbacks.h */, + 26E3EEF711A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.cpp */, + 26E3EEF811A994E800FBADB6 /* RegisterContextMacOSXFrameBackchain.h */, + 26E3EEBE11A98A1900FBADB6 /* UnwindLibUnwind.cpp */, + 26E3EEBF11A98A1900FBADB6 /* UnwindLibUnwind.h */, + 26E3EEE311A9901300FBADB6 /* UnwindMacOSXFrameBackchain.cpp */, + 26E3EEE411A9901300FBADB6 /* UnwindMacOSXFrameBackchain.h */, + ); + name = Utility; + sourceTree = ""; + }; + 26BC7C1010F1B34800F91463 /* Core */ = { + isa = PBXGroup; + children = ( + 26BC7D5010F1B77400F91463 /* Address.h */, + 26BC7E6910F1B85900F91463 /* Address.cpp */, + 26BC7D5110F1B77400F91463 /* AddressRange.h */, + 26BC7E6A10F1B85900F91463 /* AddressRange.cpp */, + 9AC7033E11752C540086C050 /* AddressResolver.h */, + 9AC7034011752C6B0086C050 /* AddressResolver.cpp */, + 9AC7033D11752C4C0086C050 /* AddressResolverFileLine.h */, + 9AC7034211752C720086C050 /* AddressResolverFileLine.cpp */, + 9AC7033F11752C590086C050 /* AddressResolverName.h */, + 9AC7034411752C790086C050 /* AddressResolverName.cpp */, + 26BC7D5210F1B77400F91463 /* ArchSpec.h */, + 26BC7E6B10F1B85900F91463 /* ArchSpec.cpp */, + 26BC7D5310F1B77400F91463 /* Args.h */, + 26BC7E6C10F1B85900F91463 /* Args.cpp */, + 26A0604711A5BC7A00F75969 /* Baton.h */, + 26A0604811A5D03C00F75969 /* Baton.cpp */, + 26BC7D5410F1B77400F91463 /* Broadcaster.h */, + 26BC7E6D10F1B85900F91463 /* Broadcaster.cpp */, + 26BC7D5510F1B77400F91463 /* ClangForward.h */, + 26BC7D5610F1B77400F91463 /* Communication.h */, + 26BC7E6E10F1B85900F91463 /* Communication.cpp */, + 26BC7D5710F1B77400F91463 /* Connection.h */, + 26BC7E6F10F1B85900F91463 /* Connection.cpp */, + 26BC7D5810F1B77400F91463 /* ConnectionFileDescriptor.h */, + 26BC7E7010F1B85900F91463 /* ConnectionFileDescriptor.cpp */, + 26BC7D7C10F1B77400F91463 /* ConstString.h */, + 26BC7E9410F1B85900F91463 /* ConstString.cpp */, + 26BC7D5910F1B77400F91463 /* DataBuffer.h */, + 26BC7D5B10F1B77400F91463 /* DataBufferHeap.h */, + 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */, + 26BC7D5C10F1B77400F91463 /* DataBufferMemoryMap.h */, + 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */, + 26BC7D5A10F1B77400F91463 /* DataExtractor.h */, + 26BC7E7110F1B85900F91463 /* DataExtractor.cpp */, + 263664941140A4C10075843B /* Debugger.h */, + 263664921140A4930075843B /* Debugger.cpp */, + 26BC7D5E10F1B77400F91463 /* Disassembler.h */, + 26BC7E7610F1B85900F91463 /* Disassembler.cpp */, + 26BC7D5F10F1B77400F91463 /* dwarf.h */, + 26BC7E7710F1B85900F91463 /* DynamicLoader.cpp */, + 26BC7D6010F1B77400F91463 /* Error.h */, + 26BC7E7810F1B85900F91463 /* Error.cpp */, + 26BC7D6110F1B77400F91463 /* Event.h */, + 26BC7E7910F1B85900F91463 /* Event.cpp */, + 26BC7D6210F1B77400F91463 /* FileSpec.h */, + 26BC7E7A10F1B85900F91463 /* FileSpec.cpp */, + 26BC7D6310F1B77400F91463 /* FileSpecList.h */, + 26BC7E7B10F1B85900F91463 /* FileSpecList.cpp */, + 26BC7D6410F1B77400F91463 /* Flags.h */, + 26BC7E7C10F1B85900F91463 /* Flags.cpp */, + 9AA69DBB118A029E00D753A0 /* InputReader.h */, + 9AA69DB5118A027A00D753A0 /* InputReader.cpp */, + 26BC7D6510F1B77400F91463 /* IOStreamMacros.h */, + 26BC7D6610F1B77400F91463 /* Language.h */, + 26BC7E7D10F1B85900F91463 /* Language.cpp */, + 26BC7D6710F1B77400F91463 /* Listener.h */, + 26BC7E7E10F1B85900F91463 /* Listener.cpp */, + 26BC7D6810F1B77400F91463 /* Log.h */, + 26BC7E7F10F1B85900F91463 /* Log.cpp */, + 26BC7D6910F1B77400F91463 /* Mangled.h */, + 26BC7E8010F1B85900F91463 /* Mangled.cpp */, + 26BC7D6A10F1B77400F91463 /* Module.h */, + 26BC7E8110F1B85900F91463 /* Module.cpp */, + 26BC7D6B10F1B77400F91463 /* ModuleChild.h */, + 26BC7E8210F1B85900F91463 /* ModuleChild.cpp */, + 26BC7D6C10F1B77400F91463 /* ModuleList.h */, + 26BC7E8310F1B85900F91463 /* ModuleList.cpp */, + 26BC7D6D10F1B77400F91463 /* Options.h */, + 26BC7E8610F1B85900F91463 /* Options.cpp */, + 26BC7D7010F1B77400F91463 /* PluginInterface.h */, + 26BC7D7110F1B77400F91463 /* PluginManager.h */, + 26BC7E8A10F1B85900F91463 /* PluginManager.cpp */, + 26BC7D7310F1B77400F91463 /* RegularExpression.h */, + 26BC7E8C10F1B85900F91463 /* RegularExpression.cpp */, + 26BC7D7410F1B77400F91463 /* Scalar.h */, + 26BC7E8D10F1B85900F91463 /* Scalar.cpp */, + 26BC7CF910F1B71400F91463 /* SearchFilter.h */, + 26BC7E1510F1B83100F91463 /* SearchFilter.cpp */, + 26BC7D7510F1B77400F91463 /* Section.h */, + 26BC7E8E10F1B85900F91463 /* Section.cpp */, + 26BC7D7610F1B77400F91463 /* SourceManager.h */, + 26BC7E8F10F1B85900F91463 /* SourceManager.cpp */, + 26BC7D7710F1B77400F91463 /* State.h */, + 26BC7E9010F1B85900F91463 /* State.cpp */, + 26BC7D7810F1B77400F91463 /* STLUtils.h */, + 26BC7D7910F1B77400F91463 /* Stream.h */, + 26BC7E9110F1B85900F91463 /* Stream.cpp */, + 26BC7D7A10F1B77400F91463 /* StreamFile.h */, + 26BC7E9210F1B85900F91463 /* StreamFile.cpp */, + 26BC7D7B10F1B77400F91463 /* StreamString.h */, + 26BC7E9310F1B85900F91463 /* StreamString.cpp */, + 9A35765E116E76A700E8ED2F /* StringList.h */, + 9A35765F116E76B900E8ED2F /* StringList.cpp */, + 26BC7D7E10F1B77400F91463 /* Timer.h */, + 26BC7E9610F1B85900F91463 /* Timer.cpp */, + 26BC7D7F10F1B77400F91463 /* TTYState.h */, + 26BC7E9710F1B85900F91463 /* TTYState.cpp */, + 268A813F115B19D000F645B0 /* UniqueCStringMap.h */, + 26BC7D8010F1B77400F91463 /* UserID.h */, + 26BC7E9810F1B85900F91463 /* UserID.cpp */, + 26BC7D8110F1B77400F91463 /* Value.h */, + 26BC7E9910F1B85900F91463 /* Value.cpp */, + 26BC7D8210F1B77400F91463 /* ValueObject.h */, + 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */, + 26BC7D8310F1B77400F91463 /* ValueObjectChild.h */, + 26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */, + 26BC7D8410F1B77400F91463 /* ValueObjectList.h */, + 26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */, + 2643343A1110F63C00CDB6C6 /* ValueObjectRegister.h */, + 264334381110F63100CDB6C6 /* ValueObjectRegister.cpp */, + 26BC7D8510F1B77400F91463 /* ValueObjectVariable.h */, + 26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */, + 26BC7D8610F1B77400F91463 /* VMRange.h */, + 26BC7E9E10F1B85900F91463 /* VMRange.cpp */, + 26B167A41123BF5500DC7B4F /* ThreadSafeValue.h */, + 263FEDA5112CC1DA00E4C208 /* ThreadSafeSTLMap.h */, + 26C81CA411335651004BDC5A /* UUID.h */, + 26C81CA511335651004BDC5A /* UUID.cpp */, + ); + name = Core; + sourceTree = ""; + }; + 26BC7C4B10F1B6C100F91463 /* Symbol */ = { + isa = PBXGroup; + children = ( + 26BC7C5510F1B6E900F91463 /* Block.h */, + 26BC7F1310F1B8EC00F91463 /* Block.cpp */, + 26BC7C5610F1B6E900F91463 /* ClangASTContext.h */, + 26BC7F1410F1B8EC00F91463 /* ClangASTContext.cpp */, + 26BC7C5710F1B6E900F91463 /* CompileUnit.h */, + 26BC7F1510F1B8EC00F91463 /* CompileUnit.cpp */, + 26BC7C5810F1B6E900F91463 /* Declaration.h */, + 26BC7F1610F1B8EC00F91463 /* Declaration.cpp */, + 26BC7C5910F1B6E900F91463 /* DWARFCallFrameInfo.h */, + 26BC7F1710F1B8EC00F91463 /* DWARFCallFrameInfo.cpp */, + 26BC7C5A10F1B6E900F91463 /* Function.h */, + 26BC7F1810F1B8EC00F91463 /* Function.cpp */, + 26BC7C5B10F1B6E900F91463 /* LineEntry.h */, + 26BC7F1910F1B8EC00F91463 /* LineEntry.cpp */, + 26BC7C5C10F1B6E900F91463 /* LineTable.h */, + 26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */, + 26BC7C5D10F1B6E900F91463 /* ObjectContainer.h */, + 26BC7C5E10F1B6E900F91463 /* ObjectFile.h */, + 26BC7F4C10F1BC1A00F91463 /* ObjectFile.cpp */, + 26BC7C5F10F1B6E900F91463 /* Symbol.h */, + 26BC7F1B10F1B8EC00F91463 /* Symbol.cpp */, + 26BC7C6010F1B6E900F91463 /* SymbolContext.h */, + 26BC7F1C10F1B8EC00F91463 /* SymbolContext.cpp */, + 26BC7C6110F1B6E900F91463 /* SymbolContextScope.h */, + 26BC7C6210F1B6E900F91463 /* SymbolFile.h */, + 26BC7F1D10F1B8EC00F91463 /* SymbolFile.cpp */, + 26BC7C6310F1B6E900F91463 /* SymbolVendor.h */, + 26BC7F1E10F1B8EC00F91463 /* SymbolVendor.mm */, + 26BC7C6410F1B6E900F91463 /* Symtab.h */, + 26BC7F1F10F1B8EC00F91463 /* Symtab.cpp */, + 26BC7C6510F1B6E900F91463 /* Type.h */, + 26BC7F2010F1B8EC00F91463 /* Type.cpp */, + 26BC7C6610F1B6E900F91463 /* TypeList.h */, + 26BC7F2110F1B8EC00F91463 /* TypeList.cpp */, + 26BC7C6710F1B6E900F91463 /* Variable.h */, + 26BC7F2210F1B8EC00F91463 /* Variable.cpp */, + 26BC7C6810F1B6E900F91463 /* VariableList.h */, + 26BC7F2310F1B8EC00F91463 /* VariableList.cpp */, + ); + name = Symbol; + sourceTree = ""; + }; + 26BC7CEB10F1B70800F91463 /* Breakpoint */ = { + isa = PBXGroup; + children = ( + 26BC7CEE10F1B71400F91463 /* Breakpoint.h */, + 26BC7E0A10F1B83100F91463 /* Breakpoint.cpp */, + 26BC7CEF10F1B71400F91463 /* BreakpointID.h */, + 26BC7E0B10F1B83100F91463 /* BreakpointID.cpp */, + 26BC7CF010F1B71400F91463 /* BreakpointIDList.h */, + 26BC7E0C10F1B83100F91463 /* BreakpointIDList.cpp */, + 26BC7CF110F1B71400F91463 /* BreakpointList.h */, + 26BC7E0D10F1B83100F91463 /* BreakpointList.cpp */, + 26BC7CF210F1B71400F91463 /* BreakpointLocation.h */, + 26BC7E0E10F1B83100F91463 /* BreakpointLocation.cpp */, + 26BC7CF310F1B71400F91463 /* BreakpointLocationCollection.h */, + 26BC7E0F10F1B83100F91463 /* BreakpointLocationCollection.cpp */, + 26BC7CF410F1B71400F91463 /* BreakpointLocationList.h */, + 26BC7E1010F1B83100F91463 /* BreakpointLocationList.cpp */, + 26BC7CF510F1B71400F91463 /* BreakpointOptions.h */, + 26BC7E1110F1B83100F91463 /* BreakpointOptions.cpp */, + 26BC7CF610F1B71400F91463 /* BreakpointResolver.h */, + 26BC7E1210F1B83100F91463 /* BreakpointResolver.cpp */, + 26D0DD5010FE554D00271C65 /* BreakpointResolverAddress.h */, + 26D0DD5310FE555900271C65 /* BreakpointResolverAddress.cpp */, + 26D0DD5110FE554D00271C65 /* BreakpointResolverFileLine.h */, + 26D0DD5410FE555900271C65 /* BreakpointResolverFileLine.cpp */, + 26D0DD5210FE554D00271C65 /* BreakpointResolverName.h */, + 26D0DD5510FE555900271C65 /* BreakpointResolverName.cpp */, + 26BC7CF710F1B71400F91463 /* BreakpointSite.h */, + 26BC7E1310F1B83100F91463 /* BreakpointSite.cpp */, + 26BC7CF810F1B71400F91463 /* BreakpointSiteList.h */, + 26BC7E1410F1B83100F91463 /* BreakpointSiteList.cpp */, + 26BC7CFA10F1B71400F91463 /* Stoppoint.h */, + 26BC7E1610F1B83100F91463 /* Stoppoint.cpp */, + 26BC7CED10F1B71400F91463 /* StoppointCallbackContext.h */, + 26BC7E0910F1B83100F91463 /* StoppointCallbackContext.cpp */, + 26BC7CFB10F1B71400F91463 /* StoppointLocation.h */, + 26BC7E1710F1B83100F91463 /* StoppointLocation.cpp */, + 26BC7CFC10F1B71400F91463 /* WatchpointLocation.h */, + 26BC7E1810F1B83100F91463 /* WatchpointLocation.cpp */, + ); + name = Breakpoint; + sourceTree = ""; + }; + 26BC7D0D10F1B71D00F91463 /* Commands */ = { + isa = PBXGroup; + children = ( + 26BC7D1010F1B76300F91463 /* CommandObjectAdd.h */, + 26BC7E2910F1B84700F91463 /* CommandObjectAdd.cpp */, + 26BC7D1110F1B76300F91463 /* CommandObjectAlias.h */, + 26BC7E2A10F1B84700F91463 /* CommandObjectAlias.cpp */, + 26BC7D1210F1B76300F91463 /* CommandObjectAppend.h */, + 26BC7E2B10F1B84700F91463 /* CommandObjectAppend.cpp */, + 4CA9637A11B6E99A00780E28 /* CommandObjectApropos.h */, + 4CA9637911B6E99A00780E28 /* CommandObjectApropos.cpp */, + 499F381E11A5B3F300F5CE02 /* CommandObjectArgs.h */, + 499F381F11A5B3F300F5CE02 /* CommandObjectArgs.cpp */, + 26BC7D1410F1B76300F91463 /* CommandObjectBreakpoint.h */, + 26BC7E2D10F1B84700F91463 /* CommandObjectBreakpoint.cpp */, + 9A42976111861A9F00FE05CD /* CommandObjectBreakpointCommand.h */, + 9A42976211861AA600FE05CD /* CommandObjectBreakpointCommand.cpp */, + 4C98D3E5118FB9B100E575D0 /* CommandObjectCall.h */, + 4C98D3E4118FB9B100E575D0 /* CommandObjectCall.cpp */, + 26BC7D1610F1B76300F91463 /* CommandObjectDelete.h */, + 26BC7E2F10F1B84700F91463 /* CommandObjectDelete.cpp */, + 26BC7D1710F1B76300F91463 /* CommandObjectDisassemble.h */, + 26BC7E3010F1B84700F91463 /* CommandObjectDisassemble.cpp */, + 26BC7D1810F1B76300F91463 /* CommandObjectExpression.h */, + 26BC7E3110F1B84700F91463 /* CommandObjectExpression.cpp */, + 26BC7D1910F1B76300F91463 /* CommandObjectFile.h */, + 26BC7E3210F1B84700F91463 /* CommandObjectFile.cpp */, + 2672D8471189055500FF4019 /* CommandObjectFrame.h */, + 2672D8461189055500FF4019 /* CommandObjectFrame.cpp */, + 26BC7D1A10F1B76300F91463 /* CommandObjectHelp.h */, + 26BC7E3310F1B84700F91463 /* CommandObjectHelp.cpp */, + 26BC7D1B10F1B76300F91463 /* CommandObjectImage.h */, + 26BC7E3410F1B84700F91463 /* CommandObjectImage.cpp */, + 26BC7D1C10F1B76300F91463 /* CommandObjectInfo.h */, + 26BC7E3510F1B84700F91463 /* CommandObjectInfo.cpp */, + 264AD83911095BBD00E0B039 /* CommandObjectLog.h */, + 264AD83711095BA600E0B039 /* CommandObjectLog.cpp */, + 26BC7D1D10F1B76300F91463 /* CommandObjectMemory.h */, + 26BC7E3610F1B84700F91463 /* CommandObjectMemory.cpp */, + 26BC7D1F10F1B76300F91463 /* CommandObjectProcess.h */, + 26BC7E3810F1B84700F91463 /* CommandObjectProcess.cpp */, + 26BC7D2010F1B76300F91463 /* CommandObjectQuit.h */, + 26BC7E3910F1B84700F91463 /* CommandObjectQuit.cpp */, + 26BC7D2210F1B76300F91463 /* CommandObjectRegister.h */, + 26BC7E3B10F1B84700F91463 /* CommandObjectRegister.cpp */, + 26BC7D2310F1B76300F91463 /* CommandObjectRemove.h */, + 26BC7E3C10F1B84700F91463 /* CommandObjectRemove.cpp */, + 26BC7D2410F1B76300F91463 /* CommandObjectScript.h */, + 26BC7E3D10F1B84700F91463 /* CommandObjectScript.cpp */, + 26BC7D2510F1B76300F91463 /* CommandObjectSelect.h */, + 26BC7E3E10F1B84700F91463 /* CommandObjectSelect.cpp */, + 26BC7D2610F1B76300F91463 /* CommandObjectSet.h */, + 26BC7E3F10F1B84700F91463 /* CommandObjectSet.cpp */, + 26BC7D2710F1B76300F91463 /* CommandObjectSettings.h */, + 26BC7E4010F1B84700F91463 /* CommandObjectSettings.cpp */, + 26BC7D2810F1B76300F91463 /* CommandObjectShow.h */, + 26BC7E4110F1B84700F91463 /* CommandObjectShow.cpp */, + 26BC7D2910F1B76300F91463 /* CommandObjectSource.h */, + 26BC7E4210F1B84700F91463 /* CommandObjectSource.cpp */, + 26BC7D2A10F1B76300F91463 /* CommandObjectSourceFile.h */, + 26BC7E4310F1B84700F91463 /* CommandObjectSourceFile.cpp */, + 26BC7D2B10F1B76300F91463 /* CommandObjectStatus.h */, + 26BC7E4410F1B84700F91463 /* CommandObjectStatus.cpp */, + 26BC7D2C10F1B76300F91463 /* CommandObjectSyntax.h */, + 26BC7E4510F1B84700F91463 /* CommandObjectSyntax.cpp */, + 269416AE119A024800FF2715 /* CommandObjectTarget.h */, + 269416AD119A024800FF2715 /* CommandObjectTarget.cpp */, + 26BC7D2D10F1B76300F91463 /* CommandObjectThread.h */, + 26BC7E4610F1B84700F91463 /* CommandObjectThread.cpp */, + 26BC7D2E10F1B76300F91463 /* CommandObjectTranslate.h */, + 26BC7E4710F1B84700F91463 /* CommandObjectTranslate.cpp */, + 9A8B4EA210FD515000C68FF2 /* CommandObjectUnalias.h */, + 9A8B4EA310FD516400C68FF2 /* CommandObjectUnalias.cpp */, + 26BC7D2F10F1B76300F91463 /* CommandObjectVariable.h */, + 26BC7E4810F1B84700F91463 /* CommandObjectVariable.cpp */, + ); + name = Commands; + sourceTree = ""; + }; + 26BC7DBE10F1B78200F91463 /* Expression */ = { + isa = PBXGroup; + children = ( + 49D7072611B5AD03001AD875 /* ClangASTSource.h */, + 49D7072811B5AD11001AD875 /* ClangASTSource.cpp */, + 26BC7DC010F1B79500F91463 /* ClangExpression.h */, + 26BC7ED510F1B86700F91463 /* ClangExpression.cpp */, + 4C98D3E0118FB98F00E575D0 /* ClangFunction.h */, + 4C98D3DA118FB96F00E575D0 /* ClangFunction.cpp */, + 49F1A74911B338AE003ED505 /* ClangExpressionDeclMap.h */, + 49F1A74511B3388F003ED505 /* ClangExpressionDeclMap.cpp */, + 26BC7DC110F1B79500F91463 /* ClangExpressionVariable.h */, + 26BC7ED610F1B86700F91463 /* ClangExpressionVariable.cpp */, + 26BC7DC210F1B79500F91463 /* ClangStmtVisitor.h */, + 26BC7ED710F1B86700F91463 /* ClangStmtVisitor.cpp */, + 26BC7DC310F1B79500F91463 /* DWARFExpression.h */, + 26BC7ED810F1B86700F91463 /* DWARFExpression.cpp */, + 4C98D3E1118FB98F00E575D0 /* RecordingMemoryManager.h */, + 4C98D3DB118FB96F00E575D0 /* RecordingMemoryManager.cpp */, + ); + name = Expression; + sourceTree = ""; + }; + 26BC7DD010F1B7C100F91463 /* Host */ = { + isa = PBXGroup; + children = ( + 26BC7EE510F1B88100F91463 /* MacOSX */, + 26BC7DD210F1B7D500F91463 /* Condition.h */, + 26BC7DD310F1B7D500F91463 /* Endian.h */, + 26BC7DD410F1B7D500F91463 /* Host.h */, + 26BC7DD510F1B7D500F91463 /* Mutex.h */, + 26BC7DD610F1B7D500F91463 /* Predicate.h */, + 2689B0A4113EE3CD00A4AEDB /* Symbols.h */, + 26B4E26E112F35F700AB3F64 /* TimeValue.h */, + 26BC7DD710F1B7D500F91463 /* Types.h */, + ); + name = Host; + sourceTree = ""; + }; + 26BC7DDF10F1B7E200F91463 /* Interpreter */ = { + isa = PBXGroup; + children = ( + 26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */, + 4C09CB73116BD98B00C7A725 /* CommandCompletions.h */, + 4C09CB74116BD98B00C7A725 /* CommandCompletions.cpp */, + 26BC7DE110F1B7F900F91463 /* CommandContext.h */, + 26BC7F0710F1B8DD00F91463 /* CommandContext.cpp */, + 26BC7DE210F1B7F900F91463 /* CommandInterpreter.h */, + 26BC7F0810F1B8DD00F91463 /* CommandInterpreter.cpp */, + 26BC7DE310F1B7F900F91463 /* CommandObject.h */, + 26BC7F0910F1B8DD00F91463 /* CommandObject.cpp */, + 26DFBC50113B48D600DD817F /* CommandObjectCrossref.h */, + 26DFBC57113B48F300DD817F /* CommandObjectCrossref.cpp */, + 26DFBC51113B48D600DD817F /* CommandObjectMultiword.h */, + 26DFBC58113B48F300DD817F /* CommandObjectMultiword.cpp */, + 26DFBC52113B48D600DD817F /* CommandObjectRegexCommand.h */, + 26DFBC59113B48F300DD817F /* CommandObjectRegexCommand.cpp */, + 26BC7DE410F1B7F900F91463 /* CommandReturnObject.h */, + 26BC7F0A10F1B8DD00F91463 /* CommandReturnObject.cpp */, + 26BC7DE510F1B7F900F91463 /* ScriptInterpreter.h */, + 9A82010B10FFB49800182560 /* ScriptInterpreter.cpp */, + 9A2771FB1135A35C00E6ADB6 /* ScriptInterpreterNone.h */, + 9A2771FC1135A37500E6ADB6 /* ScriptInterpreterNone.cpp */, + 26BC7DE610F1B7F900F91463 /* ScriptInterpreterPython.h */, + 26BC7F0C10F1B8DD00F91463 /* ScriptInterpreterPython.cpp */, + 26BC7DE710F1B7F900F91463 /* StateVariable.h */, + 26BC7F0B10F1B8DD00F91463 /* StateVariable.cpp */, + ); + name = Interpreter; + sourceTree = ""; + }; + 26BC7DEF10F1B80200F91463 /* Target */ = { + isa = PBXGroup; + children = ( + 497E7B331188ED300065CCA1 /* ABI.h */, + 497E7B9D1188F6690065CCA1 /* ABI.cpp */, + 26BC7DF110F1B81A00F91463 /* DynamicLoader.h */, + 26BC7DF210F1B81A00F91463 /* ExecutionContext.h */, + 26BC7F3510F1B90C00F91463 /* ExecutionContext.cpp */, + 26DAFD9711529BC7005A394E /* ExecutionContextScope.h */, + 49BF48E011ADF37D008863BD /* ObjCObjectPrinter.h */, + 49BF48DC11ADF356008863BD /* ObjCObjectPrinter.cpp */, + 495BBACF119A0DE700418BEA /* PathMappingList.h */, + 495BBACB119A0DBE00418BEA /* PathMappingList.cpp */, + 26BC7DF310F1B81A00F91463 /* Process.h */, + 26BC7F3610F1B90C00F91463 /* Process.cpp */, + 26BC7DF410F1B81A00F91463 /* RegisterContext.h */, + 26BC7F3710F1B90C00F91463 /* RegisterContext.cpp */, + 26BC7DF510F1B81A00F91463 /* StackFrame.h */, + 26BC7F3810F1B90C00F91463 /* StackFrame.cpp */, + 26BC7DF610F1B81A00F91463 /* StackFrameList.h */, + 26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */, + 26BC7DF710F1B81A00F91463 /* StackID.h */, + 26BC7F3A10F1B90C00F91463 /* StackID.cpp */, + 26BC7DF810F1B81A00F91463 /* Target.h */, + 26BC7F3B10F1B90C00F91463 /* Target.cpp */, + 26BC7DF910F1B81A00F91463 /* TargetList.h */, + 26BC7F3C10F1B90C00F91463 /* TargetList.cpp */, + 26BC7DFA10F1B81A00F91463 /* Thread.h */, + 26BC7F3D10F1B90C00F91463 /* Thread.cpp */, + 26BC7DFB10F1B81A00F91463 /* ThreadList.h */, + 26BC7F3E10F1B90C00F91463 /* ThreadList.cpp */, + 26BC7DFC10F1B81A00F91463 /* ThreadPlan.h */, + 26BC7F3F10F1B90C00F91463 /* ThreadPlan.cpp */, + 260C847F10F50F0A00BB2B04 /* ThreadPlanBase.h */, + 260C847110F50EFC00BB2B04 /* ThreadPlanBase.cpp */, + 49EC3E9C118F90D400B1265E /* ThreadPlanCallFunction.h */, + 49EC3E98118F90AC00B1265E /* ThreadPlanCallFunction.cpp */, + 260C847E10F50F0A00BB2B04 /* ThreadPlanContinue.h */, + 260C847010F50EFC00BB2B04 /* ThreadPlanContinue.cpp */, + 4C43DEF9110641F300E55CBF /* ThreadPlanShouldStopHere.h */, + 4C43DEFA110641F300E55CBF /* ThreadPlanShouldStopHere.cpp */, + 260C848010F50F0A00BB2B04 /* ThreadPlanStepInstruction.h */, + 260C847210F50EFC00BB2B04 /* ThreadPlanStepInstruction.cpp */, + 260C848110F50F0A00BB2B04 /* ThreadPlanStepOut.h */, + 260C847310F50EFC00BB2B04 /* ThreadPlanStepOut.cpp */, + 260C848210F50F0A00BB2B04 /* ThreadPlanStepOverBreakpoint.h */, + 260C847410F50EFC00BB2B04 /* ThreadPlanStepOverBreakpoint.cpp */, + 260C848410F50F0A00BB2B04 /* ThreadPlanStepRange.h */, + 260C847610F50EFC00BB2B04 /* ThreadPlanStepRange.cpp */, + 4C43DF8511069BFD00E55CBF /* ThreadPlanStepInRange.h */, + 4C43DF8911069C3200E55CBF /* ThreadPlanStepInRange.cpp */, + 4C43DF8611069BFD00E55CBF /* ThreadPlanStepOverRange.h */, + 4C43DF8A11069C3200E55CBF /* ThreadPlanStepOverRange.cpp */, + 4CAFCE001101216B00CA63DB /* ThreadPlanRunToAddress.h */, + 4CAFCE031101218900CA63DB /* ThreadPlanRunToAddress.cpp */, + 260C848310F50F0A00BB2B04 /* ThreadPlanStepThrough.h */, + 260C847510F50EFC00BB2B04 /* ThreadPlanStepThrough.cpp */, + 4CEDAED311754F5E00E875A6 /* ThreadPlanStepUntil.h */, + 2660D9FE11922A7F00958FBD /* ThreadPlanStepUntil.cpp */, + 4C00986F11500B4300F316B0 /* UnixSignals.h */, + 4C00987011500B4300F316B0 /* UnixSignals.cpp */, + 26E3EEBD11A9870400FBADB6 /* Unwind.h */, + ); + name = Target; + sourceTree = ""; + }; + 26BC7EE510F1B88100F91463 /* MacOSX */ = { + isa = PBXGroup; + children = ( + 26BC7EED10F1B8AD00F91463 /* CFCBundle.cpp */, + 26BC7EEE10F1B8AD00F91463 /* CFCBundle.h */, + 26BC7EEF10F1B8AD00F91463 /* CFCData.cpp */, + 26BC7EF010F1B8AD00F91463 /* CFCData.h */, + 26BC7EF110F1B8AD00F91463 /* CFCMutableArray.cpp */, + 26BC7EF210F1B8AD00F91463 /* CFCMutableArray.h */, + 26BC7EF310F1B8AD00F91463 /* CFCMutableDictionary.cpp */, + 26BC7EF410F1B8AD00F91463 /* CFCMutableDictionary.h */, + 26BC7EF510F1B8AD00F91463 /* CFCMutableSet.cpp */, + 26BC7EF610F1B8AD00F91463 /* CFCMutableSet.h */, + 26BC7EF710F1B8AD00F91463 /* CFCReleaser.h */, + 26BC7EF810F1B8AD00F91463 /* CFCString.cpp */, + 26BC7EF910F1B8AD00F91463 /* CFCString.h */, + 26BC7EE710F1B88F00F91463 /* Condition.cpp */, + 26BC7EE810F1B88F00F91463 /* Host.mm */, + 26BC7EE910F1B88F00F91463 /* Mutex.cpp */, + 2689B0B5113EE47E00A4AEDB /* Symbols.cpp */, + 26B4E28B112F5DCD00AB3F64 /* TimeValue.cpp */, + ); + name = MacOSX; + sourceTree = ""; + }; + 26C9DF02113C5B80006B0F94 /* Include */ = { + isa = PBXGroup; + children = ( + 26C9DF03113C5B93006B0F94 /* lldb */, + ); + name = Include; + sourceTree = ""; + }; + 26F5C22410F3D950009D5894 /* Tools */ = { + isa = PBXGroup; + children = ( + 265E9BE0115C2B8500D0DCCB /* debugserver */, + 26F5C22510F3D956009D5894 /* Driver */, + ); + name = Tools; + sourceTree = ""; + usesTabs = 0; + }; + 26F5C22510F3D956009D5894 /* Driver */ = { + isa = PBXGroup; + children = ( + 26F5C27210F3D9E4009D5894 /* lldb-Info.plist */, + 26F5C27410F3D9E4009D5894 /* Driver.h */, + 26F5C27310F3D9E4009D5894 /* Driver.cpp */, + 26F5C27610F3D9E4009D5894 /* IOChannel.h */, + 26F5C27510F3D9E4009D5894 /* IOChannel.cpp */, + ); + name = Driver; + sourceTree = ""; + }; + 26F5C32810F3DF7D009D5894 /* Libraries */ = { + isa = PBXGroup; + children = ( + 26F5C39010F3FA26009D5894 /* CoreFoundation.framework */, + 265ABF6210F42EE900531910 /* DebugSymbols.framework */, + 260C876910F538E700BB2B04 /* Foundation.framework */, + 26F5C32A10F3DFDD009D5894 /* libedit.dylib */, + 26F5C37410F3F61B009D5894 /* libobjc.dylib */, + 26F5C32410F3DF23009D5894 /* libpython2.6.dylib */, + 26F5C32B10F3DFDD009D5894 /* libtermcap.dylib */, + ); + name = Libraries; + sourceTree = ""; + usesTabs = 0; + }; + 493C63D711891A8000914D5E /* ABI */ = { + isa = PBXGroup; + children = ( + 497650CD11A21BD8008DDB57 /* MacOSX-i386 */, + 493C63EA11891FCD00914D5E /* SysV-x86_64 */, + ); + name = ABI; + sourceTree = ""; + }; + 493C63EA11891FCD00914D5E /* SysV-x86_64 */ = { + isa = PBXGroup; + children = ( + 493C63F11189203300914D5E /* ABISysV_x86_64.cpp */, + 493C63F01189203300914D5E /* ABISysV_x86_64.h */, + ); + name = "SysV-x86_64"; + sourceTree = ""; + }; + 497650CD11A21BD8008DDB57 /* MacOSX-i386 */ = { + isa = PBXGroup; + children = ( + 497650CE11A21BEE008DDB57 /* ABIMacOSX_i386.cpp */, + 497650CF11A21BEE008DDB57 /* ABIMacOSX_i386.h */, + ); + name = "MacOSX-i386"; + sourceTree = ""; + }; + 4CEE62F71145F1C70064CF93 /* GDB Remote */ = { + isa = PBXGroup; + children = ( + 26FE25231146CADE00F4085A /* GDBRemoteCommunication.h */, + 26FE25221146CADE00F4085A /* GDBRemoteCommunication.cpp */, + 261E18CC1148966100BADCD3 /* GDBRemoteRegisterContext.h */, + 261E18CD1148966100BADCD3 /* GDBRemoteRegisterContext.cpp */, + 4CEE62FB1145F2130064CF93 /* ProcessGDBRemote.h */, + 4CEE62FA1145F2130064CF93 /* ProcessGDBRemote.cpp */, + 4CEE62FD1145F2130064CF93 /* ProcessGDBRemoteLog.h */, + 4CEE62FC1145F2130064CF93 /* ProcessGDBRemoteLog.cpp */, + 4CEE62FF1145F2130064CF93 /* ThreadGDBRemote.h */, + 4CEE62FE1145F2130064CF93 /* ThreadGDBRemote.cpp */, + ); + name = "GDB Remote"; + path = ../../..; + sourceTree = ""; + }; + 9654F79F1197DA3F00F72B43 /* libunwind */ = { + isa = PBXGroup; + children = ( + 9654F7A01197DA3F00F72B43 /* include */, + 9654F7A51197DA3F00F72B43 /* src */, + ); + name = libunwind; + path = Utility/libunwind; + sourceTree = ""; + }; + 9654F7A01197DA3F00F72B43 /* include */ = { + isa = PBXGroup; + children = ( + 9654F7A11197DA3F00F72B43 /* libunwind.h */, + 9654F7A21197DA3F00F72B43 /* mach-o */, + 9654F7A41197DA3F00F72B43 /* unwind.h */, + ); + path = include; + sourceTree = ""; + }; + 9654F7A21197DA3F00F72B43 /* mach-o */ = { + isa = PBXGroup; + children = ( + 9654F7A31197DA3F00F72B43 /* compact_unwind_encoding.h */, + ); + path = "mach-o"; + sourceTree = ""; + }; + 9654F7A51197DA3F00F72B43 /* src */ = { + isa = PBXGroup; + children = ( + 9654F7A61197DA3F00F72B43 /* AddressSpace.hpp */, + 9654F7A71197DA3F00F72B43 /* ArchDefaultUnwinder.hpp */, + 9654F7A81197DA3F00F72B43 /* AssemblyInstructions.hpp */, + 9654F7A91197DA3F00F72B43 /* AssemblyParser.hpp */, + 9654F7AA1197DA3F00F72B43 /* CompactUnwinder.hpp */, + 9654F7AB1197DA3F00F72B43 /* dwarf2.h */, + 9654F7AC1197DA3F00F72B43 /* DwarfInstructions.hpp */, + 9654F7AD1197DA3F00F72B43 /* DwarfParser.hpp */, + 9654F7AE1197DA3F00F72B43 /* FileAbstraction.hpp */, + 9654F7AF1197DA3F00F72B43 /* InternalMacros.h */, + 9654F7B21197DA3F00F72B43 /* libunwind_priv.h */, + 9654F7B31197DA3F00F72B43 /* libuwind.cxx */, + 9654F7B41197DA3F00F72B43 /* Registers.hpp */, + 9654F7B51197DA3F00F72B43 /* Registers.s */, + 9654F7B61197DA3F00F72B43 /* RemoteDebuggerDummyUnwinder.hpp */, + 9654F7B71197DA3F00F72B43 /* RemoteProcInfo.hpp */, + 9654F7B81197DA3F00F72B43 /* RemoteRegisterMap.hpp */, + 9654F7B91197DA3F00F72B43 /* RemoteUnwindProfile.h */, + 9654F7BA1197DA3F00F72B43 /* unw_getcontext.s */, + 9654F7BD1197DA3F00F72B43 /* UnwindCursor.hpp */, + ); + path = src; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 26680202115FD0ED008E1FE4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 26680219115FD13D008E1FE4 /* SBBreakpoint.h in Headers */, + 2668021A115FD13D008E1FE4 /* SBBreakpointLocation.h in Headers */, + 2668021B115FD13D008E1FE4 /* SBBroadcaster.h in Headers */, + 2668021C115FD13D008E1FE4 /* SBCommandContext.h in Headers */, + 2668021D115FD13D008E1FE4 /* SBCommandInterpreter.h in Headers */, + 2668021E115FD13D008E1FE4 /* SBCommandReturnObject.h in Headers */, + 2668021F115FD13D008E1FE4 /* SBCommunication.h in Headers */, + 26680220115FD13D008E1FE4 /* SBDebugger.h in Headers */, + 26680221115FD13D008E1FE4 /* SBDefines.h in Headers */, + 26680222115FD13D008E1FE4 /* SBError.h in Headers */, + 26680223115FD13D008E1FE4 /* SBEvent.h in Headers */, + 26680224115FD13D008E1FE4 /* SBFileSpec.h in Headers */, + 26680225115FD13D008E1FE4 /* SBFrame.h in Headers */, + 26680227115FD13D008E1FE4 /* SBListener.h in Headers */, + 2668022A115FD13D008E1FE4 /* SBProcess.h in Headers */, + 2668022B115FD13D008E1FE4 /* SBSourceManager.h in Headers */, + 2668022C115FD13D008E1FE4 /* SBTarget.h in Headers */, + 2668022E115FD13D008E1FE4 /* SBThread.h in Headers */, + 2668020E115FD12C008E1FE4 /* lldb-defines.h in Headers */, + 2668020F115FD12C008E1FE4 /* lldb-enumerations.h in Headers */, + 26680214115FD12C008E1FE4 /* lldb-types.h in Headers */, + 26DE1E6B11616C2E00A093E2 /* lldb-forward-rtti.h in Headers */, + 26DE1E6C11616C2E00A093E2 /* lldb-forward.h in Headers */, + 26DE204111618AB900A093E2 /* SBSymbolContext.h in Headers */, + 26DE204311618ACA00A093E2 /* SBAddress.h in Headers */, + 26DE204F11618E9800A093E2 /* SBModule.h in Headers */, + 26DE205311618FAC00A093E2 /* SBFunction.h in Headers */, + 26DE205511618FB800A093E2 /* SBCompileUnit.h in Headers */, + 26DE205711618FC500A093E2 /* SBBlock.h in Headers */, + 26DE205911618FE700A093E2 /* SBLineEntry.h in Headers */, + 26DE205B11618FF600A093E2 /* SBSymbol.h in Headers */, + 9A19A6AF1163BBB200E0D453 /* SBValue.h in Headers */, + 2617447A11685869005ADD65 /* SBType.h in Headers */, + 9A357583116CFDEE00E8ED2F /* SBValueList.h in Headers */, + 9A357671116E7B5200E8ED2F /* SBStringList.h in Headers */, + 9A3576A8116E9AB700E8ED2F /* SBHostOS.h in Headers */, + 9AC7038E117674FB0086C050 /* SBInstruction.h in Headers */, + 9AC70390117675270086C050 /* SBInstructionList.h in Headers */, + 26B42B1F1187A92B0079C8C8 /* lldb-include.h in Headers */, + 26B42C4D1187ABA50079C8C8 /* LLDB.h in Headers */, + 9AA69DAF118A023300D753A0 /* SBInputReader.h in Headers */, + 49F1A74A11B338AE003ED505 /* ClangExpressionDeclMap.h in Headers */, + 49D7072711B5AD03001AD875 /* ClangASTSource.h in Headers */, + 4CA9637C11B6E99A00780E28 /* CommandObjectApropos.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 26680206115FD0ED008E1FE4 /* LLDB */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2668020B115FD0EE008E1FE4 /* Build configuration list for PBXNativeTarget "LLDB" */; + buildPhases = ( + 26D5B06111B07468009A862E /* Build llvm and clang */, + 26D5B06311B074CF009A862E /* Build swig wrapper classes */, + 26680202115FD0ED008E1FE4 /* Headers */, + 26680203115FD0ED008E1FE4 /* Resources */, + 26680204115FD0ED008E1FE4 /* Sources */, + 26680205115FD0ED008E1FE4 /* Frameworks */, + 9A19ACE2116563A700E0D453 /* Finish swig wrapper classes (lldb) */, + ); + buildRules = ( + ); + dependencies = ( + 262CFC7211A450CB00946C6C /* PBXTargetDependency */, + ); + name = LLDB; + productName = LLDB; + productReference = 26680207115FD0ED008E1FE4 /* LLDB.framework */; + productType = "com.apple.product-type.framework"; + }; + 26F5C26910F3D9A4009D5894 /* lldb-tool */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26F5C26E10F3D9C5009D5894 /* Build configuration list for PBXNativeTarget "lldb-tool" */; + buildPhases = ( + 26F5C26710F3D9A4009D5894 /* Sources */, + 26F5C26810F3D9A4009D5894 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 26CE0610115C438C0022F371 /* PBXTargetDependency */, + 266803621160110D008E1FE4 /* PBXTargetDependency */, + ); + name = "lldb-tool"; + productName = lldb; + productReference = 26F5C26A10F3D9A4009D5894 /* lldb */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "lldb" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* lldb */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 265E9BE2115C2BAA00D0DCCB /* Products */; + ProjectRef = 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 26F5C26910F3D9A4009D5894 /* lldb-tool */, + 26680206115FD0ED008E1FE4 /* LLDB */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 26CE05A0115C31E50022F371 /* debugserver */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = debugserver; + remoteRef = 26CE059F115C31E50022F371 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 26680203115FD0ED008E1FE4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 262CFC7711A4510000946C6C /* debugserver in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 26D5B06111B07468009A862E /* Build llvm and clang */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(LLVM_BUILD_DIR)", + "$(SRCROOT)/scripts/build-llvm.pl", + ); + name = "Build llvm and clang"; + outputPaths = ( + "$(CONFIGURATION_BUILD_DIR)/libEnhancedDisassembly.dylib", + "$(LLVM_BUILD_DIR)/libllvmclang.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "perl $SRCROOT/scripts/build-llvm.pl"; + }; + 26D5B06311B074CF009A862E /* Build swig wrapper classes */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/scripts/lldb.swig", + "$(SRCROOT)/source/LLDBWrapPython.cpp", + ); + name = "Build swig wrapper classes"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "sh $SRCROOT/scripts/build-swig-wrapper-classes.sh\n"; + }; + 9A19ACE2116563A700E0D453 /* Finish swig wrapper classes (lldb) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/lldb.py", + "$(SRCROOT)/source/Interpreter/embedded_interpreter.py", + "$(SRCROOT)/scripts/Python/LLDBWrapPython.o", + ); + name = "Finish swig wrapper classes (lldb)"; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/LLDB.framework/Resources/lldb.py", + "$(BUILT_PRODUCTS_DIR)/LLDB.framework/Resources/_lldb.so", + "$(BUILT_PRODUCTS_DIR)/LLDB.framework/Resources/embedded_interpreter.py", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "sh $SRCROOT/scripts/finish-swig-wrapper-classes.sh"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 26680204115FD0ED008E1FE4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 26D5B06511B07550009A862E /* StoppointCallbackContext.cpp in Sources */, + 26D5B06611B07550009A862E /* Breakpoint.cpp in Sources */, + 26D5B06711B07550009A862E /* BreakpointID.cpp in Sources */, + 26D5B06811B07550009A862E /* BreakpointIDList.cpp in Sources */, + 26D5B06911B07550009A862E /* BreakpointList.cpp in Sources */, + 26D5B06A11B07550009A862E /* BreakpointLocation.cpp in Sources */, + 26D5B06B11B07550009A862E /* BreakpointLocationCollection.cpp in Sources */, + 26D5B06C11B07550009A862E /* BreakpointLocationList.cpp in Sources */, + 26D5B06D11B07550009A862E /* BreakpointOptions.cpp in Sources */, + 26D5B06E11B07550009A862E /* BreakpointResolver.cpp in Sources */, + 26D5B06F11B07550009A862E /* BreakpointSite.cpp in Sources */, + 26D5B07011B07550009A862E /* BreakpointSiteList.cpp in Sources */, + 26D5B07111B07550009A862E /* SearchFilter.cpp in Sources */, + 26D5B07211B07550009A862E /* Stoppoint.cpp in Sources */, + 26D5B07311B07550009A862E /* StoppointLocation.cpp in Sources */, + 26D5B07411B07550009A862E /* WatchpointLocation.cpp in Sources */, + 26D5B07511B07550009A862E /* CommandObjectAlias.cpp in Sources */, + 26D5B07611B07550009A862E /* CommandObjectAppend.cpp in Sources */, + 26D5B07711B07550009A862E /* CommandObjectBreakpoint.cpp in Sources */, + 26D5B07811B07550009A862E /* CommandObjectDelete.cpp in Sources */, + 26D5B07911B07550009A862E /* CommandObjectDisassemble.cpp in Sources */, + 26D5B07A11B07550009A862E /* CommandObjectExpression.cpp in Sources */, + 26D5B07B11B07550009A862E /* CommandObjectFile.cpp in Sources */, + 26D5B07C11B07550009A862E /* CommandObjectHelp.cpp in Sources */, + 26D5B07D11B07550009A862E /* CommandObjectImage.cpp in Sources */, + 26D5B07E11B07550009A862E /* CommandObjectInfo.cpp in Sources */, + 26D5B07F11B07550009A862E /* CommandObjectMemory.cpp in Sources */, + 26D5B08011B07550009A862E /* CommandObjectProcess.cpp in Sources */, + 26D5B08111B07550009A862E /* CommandObjectQuit.cpp in Sources */, + 26D5B08211B07550009A862E /* CommandObjectRegister.cpp in Sources */, + 26D5B08311B07550009A862E /* CommandObjectScript.cpp in Sources */, + 26D5B08411B07550009A862E /* CommandObjectSelect.cpp in Sources */, + 26D5B08511B07550009A862E /* CommandObjectSet.cpp in Sources */, + 26D5B08611B07550009A862E /* CommandObjectSettings.cpp in Sources */, + 26D5B08711B07550009A862E /* CommandObjectShow.cpp in Sources */, + 26D5B08811B07550009A862E /* CommandObjectSource.cpp in Sources */, + 26D5B08911B07550009A862E /* CommandObjectSourceFile.cpp in Sources */, + 26D5B08A11B07550009A862E /* CommandObjectStatus.cpp in Sources */, + 26D5B08B11B07550009A862E /* CommandObjectSyntax.cpp in Sources */, + 26D5B08C11B07550009A862E /* CommandObjectThread.cpp in Sources */, + 26D5B08D11B07550009A862E /* CommandObjectVariable.cpp in Sources */, + 26D5B08E11B07550009A862E /* Address.cpp in Sources */, + 26D5B08F11B07550009A862E /* AddressRange.cpp in Sources */, + 26D5B09011B07550009A862E /* ArchSpec.cpp in Sources */, + 26D5B09111B07550009A862E /* Args.cpp in Sources */, + 26D5B09211B07550009A862E /* Broadcaster.cpp in Sources */, + 26D5B09311B07550009A862E /* Communication.cpp in Sources */, + 26D5B09411B07550009A862E /* Connection.cpp in Sources */, + 26D5B09511B07550009A862E /* ConnectionFileDescriptor.cpp in Sources */, + 26D5B09611B07550009A862E /* DataExtractor.cpp in Sources */, + 26D5B09711B07550009A862E /* DataBufferHeap.cpp in Sources */, + 26D5B09811B07550009A862E /* DataBufferMemoryMap.cpp in Sources */, + 26D5B09911B07550009A862E /* lldb.cpp in Sources */, + 26D5B09A11B07550009A862E /* lldb-log.cpp in Sources */, + 26D5B09B11B07550009A862E /* Disassembler.cpp in Sources */, + 26D5B09C11B07550009A862E /* DynamicLoader.cpp in Sources */, + 26D5B09D11B07550009A862E /* Error.cpp in Sources */, + 26D5B09E11B07550009A862E /* Event.cpp in Sources */, + 26D5B09F11B07550009A862E /* FileSpec.cpp in Sources */, + 26D5B0A011B07550009A862E /* FileSpecList.cpp in Sources */, + 26D5B0A111B07550009A862E /* Flags.cpp in Sources */, + 26D5B0A211B07550009A862E /* Language.cpp in Sources */, + 26D5B0A311B07550009A862E /* Listener.cpp in Sources */, + 26D5B0A411B07550009A862E /* Log.cpp in Sources */, + 26D5B0A511B07550009A862E /* Mangled.cpp in Sources */, + 26D5B0A611B07550009A862E /* Module.cpp in Sources */, + 26D5B0A711B07550009A862E /* ModuleChild.cpp in Sources */, + 26D5B0A811B07550009A862E /* ModuleList.cpp in Sources */, + 26D5B0A911B07550009A862E /* Options.cpp in Sources */, + 26D5B0AA11B07550009A862E /* PluginManager.cpp in Sources */, + 26D5B0AB11B07550009A862E /* RegularExpression.cpp in Sources */, + 26D5B0AC11B07550009A862E /* Scalar.cpp in Sources */, + 26D5B0AD11B07550009A862E /* Section.cpp in Sources */, + 26D5B0AE11B07550009A862E /* SourceManager.cpp in Sources */, + 26D5B0AF11B07550009A862E /* State.cpp in Sources */, + 26D5B0B011B07550009A862E /* Stream.cpp in Sources */, + 26D5B0B111B07550009A862E /* StreamFile.cpp in Sources */, + 26D5B0B211B07550009A862E /* StreamString.cpp in Sources */, + 26D5B0B311B07550009A862E /* ConstString.cpp in Sources */, + 26D5B0B411B07550009A862E /* Timer.cpp in Sources */, + 26D5B0B511B07550009A862E /* TTYState.cpp in Sources */, + 26D5B0B611B07550009A862E /* UserID.cpp in Sources */, + 26D5B0B711B07550009A862E /* Value.cpp in Sources */, + 26D5B0B811B07550009A862E /* ValueObject.cpp in Sources */, + 26D5B0B911B07550009A862E /* ValueObjectChild.cpp in Sources */, + 26D5B0BA11B07550009A862E /* ValueObjectList.cpp in Sources */, + 26D5B0BB11B07550009A862E /* ValueObjectVariable.cpp in Sources */, + 26D5B0BC11B07550009A862E /* VMRange.cpp in Sources */, + 26D5B0BD11B07550009A862E /* ClangExpression.cpp in Sources */, + 26D5B0BE11B07550009A862E /* ClangExpressionVariable.cpp in Sources */, + 26D5B0BF11B07550009A862E /* ClangStmtVisitor.cpp in Sources */, + 26D5B0C011B07550009A862E /* DWARFExpression.cpp in Sources */, + 26D5B0C111B07550009A862E /* Condition.cpp in Sources */, + 26D5B0C211B07550009A862E /* Host.mm in Sources */, + 26D5B0C311B07550009A862E /* Mutex.cpp in Sources */, + 26D5B0C411B07550009A862E /* CFCBundle.cpp in Sources */, + 26D5B0C511B07550009A862E /* CFCData.cpp in Sources */, + 26D5B0C611B07550009A862E /* CFCMutableArray.cpp in Sources */, + 26D5B0C711B07550009A862E /* CFCMutableDictionary.cpp in Sources */, + 26D5B0C811B07550009A862E /* CFCMutableSet.cpp in Sources */, + 26D5B0C911B07550009A862E /* CFCString.cpp in Sources */, + 26D5B0CA11B07550009A862E /* CommandContext.cpp in Sources */, + 26D5B0CB11B07550009A862E /* CommandInterpreter.cpp in Sources */, + 26D5B0CC11B07550009A862E /* CommandObject.cpp in Sources */, + 26D5B0CD11B07550009A862E /* CommandReturnObject.cpp in Sources */, + 26D5B0CE11B07550009A862E /* StateVariable.cpp in Sources */, + 26D5B0CF11B07550009A862E /* ScriptInterpreterPython.cpp in Sources */, + 26D5B0D011B07550009A862E /* Block.cpp in Sources */, + 26D5B0D111B07550009A862E /* ClangASTContext.cpp in Sources */, + 26D5B0D211B07550009A862E /* CompileUnit.cpp in Sources */, + 26D5B0D311B07550009A862E /* Declaration.cpp in Sources */, + 26D5B0D411B07550009A862E /* DWARFCallFrameInfo.cpp in Sources */, + 26D5B0D511B07550009A862E /* Function.cpp in Sources */, + 26D5B0D611B07550009A862E /* LineEntry.cpp in Sources */, + 26D5B0D711B07550009A862E /* LineTable.cpp in Sources */, + 26D5B0D811B07550009A862E /* Symbol.cpp in Sources */, + 26D5B0D911B07550009A862E /* SymbolContext.cpp in Sources */, + 26D5B0DA11B07550009A862E /* SymbolFile.cpp in Sources */, + 26D5B0DB11B07550009A862E /* SymbolVendor.mm in Sources */, + 26D5B0DC11B07550009A862E /* Symtab.cpp in Sources */, + 26D5B0DD11B07550009A862E /* Type.cpp in Sources */, + 26D5B0DE11B07550009A862E /* TypeList.cpp in Sources */, + 26D5B0DF11B07550009A862E /* Variable.cpp in Sources */, + 26D5B0E011B07550009A862E /* VariableList.cpp in Sources */, + 26D5B0E111B07550009A862E /* ExecutionContext.cpp in Sources */, + 26D5B0E211B07550009A862E /* Process.cpp in Sources */, + 26D5B0E311B07550009A862E /* RegisterContext.cpp in Sources */, + 26D5B0E411B07550009A862E /* StackFrame.cpp in Sources */, + 26D5B0E511B07550009A862E /* StackFrameList.cpp in Sources */, + 26D5B0E611B07550009A862E /* StackID.cpp in Sources */, + 26D5B0E711B07550009A862E /* Target.cpp in Sources */, + 26D5B0E811B07550009A862E /* TargetList.cpp in Sources */, + 26D5B0E911B07550009A862E /* Thread.cpp in Sources */, + 26D5B0EA11B07550009A862E /* ThreadList.cpp in Sources */, + 26D5B0EB11B07550009A862E /* ThreadPlan.cpp in Sources */, + 26D5B0EC11B07550009A862E /* ObjectFile.cpp in Sources */, + 26D5B0ED11B07550009A862E /* ThreadPlanContinue.cpp in Sources */, + 26D5B0EE11B07550009A862E /* ThreadPlanBase.cpp in Sources */, + 26D5B0EF11B07550009A862E /* ThreadPlanStepInstruction.cpp in Sources */, + 26D5B0F011B07550009A862E /* ThreadPlanStepOut.cpp in Sources */, + 26D5B0F111B07550009A862E /* ThreadPlanStepOverBreakpoint.cpp in Sources */, + 26D5B0F211B07550009A862E /* ThreadPlanStepThrough.cpp in Sources */, + 26D5B0F311B07550009A862E /* ThreadPlanStepRange.cpp in Sources */, + 26D5B0F411B07550009A862E /* DynamicLoaderMacOSXDYLD.cpp in Sources */, + 26D5B0F511B07550009A862E /* DynamicLoaderMacOSXDYLDLog.cpp in Sources */, + 26D5B0F611B07550009A862E /* ObjectContainerUniversalMachO.cpp in Sources */, + 26D5B0F711B07550009A862E /* ObjectFileELF.cpp in Sources */, + 26D5B0F811B07550009A862E /* ObjectFileMachO.cpp in Sources */, + 26D5B0F911B07550009A862E /* MachException.cpp in Sources */, + 26D5B0FA11B07550009A862E /* MachTask.cpp in Sources */, + 26D5B0FB11B07550009A862E /* MachThreadContext_arm.cpp in Sources */, + 26D5B0FC11B07550009A862E /* MachThreadContext_i386.cpp in Sources */, + 26D5B0FD11B07550009A862E /* MachThreadContext_x86_64.cpp in Sources */, + 26D5B0FE11B07550009A862E /* MachVMMemory.cpp in Sources */, + 26D5B0FF11B07550009A862E /* MachVMRegion.cpp in Sources */, + 26D5B10011B07550009A862E /* ProcessControl-mig.defs in Sources */, + 26D5B10111B07550009A862E /* ProcessMacOSX.cpp in Sources */, + 26D5B10211B07550009A862E /* ProcessMacOSXLog.cpp in Sources */, + 26D5B10311B07550009A862E /* RegisterContextMach_arm.cpp in Sources */, + 26D5B10411B07550009A862E /* RegisterContextMach_i386.cpp in Sources */, + 26D5B10511B07550009A862E /* RegisterContextMach_x86_64.cpp in Sources */, + 26D5B10611B07550009A862E /* ThreadMacOSX.cpp in Sources */, + 26D5B10711B07550009A862E /* DWARFAbbreviationDeclaration.cpp in Sources */, + 26D5B10811B07550009A862E /* DWARFCompileUnit.cpp in Sources */, + 26D5B10911B07550009A862E /* DWARFDebugAbbrev.cpp in Sources */, + 26D5B10A11B07550009A862E /* DWARFDebugAranges.cpp in Sources */, + 26D5B10B11B07550009A862E /* DWARFDebugArangeSet.cpp in Sources */, + 26D5B10C11B07550009A862E /* DWARFDebugInfo.cpp in Sources */, + 26D5B10D11B07550009A862E /* DWARFDebugInfoEntry.cpp in Sources */, + 26D5B10E11B07550009A862E /* DWARFDebugLine.cpp in Sources */, + 26D5B10F11B07550009A862E /* DWARFDebugMacinfo.cpp in Sources */, + 26D5B11011B07550009A862E /* DWARFDebugMacinfoEntry.cpp in Sources */, + 26D5B11111B07550009A862E /* DWARFDebugPubnames.cpp in Sources */, + 26D5B11211B07550009A862E /* DWARFDebugPubnamesSet.cpp in Sources */, + 26D5B11311B07550009A862E /* DWARFDebugRanges.cpp in Sources */, + 26D5B11411B07550009A862E /* DWARFDefines.c in Sources */, + 26D5B11511B07550009A862E /* DWARFDIECollection.cpp in Sources */, + 26D5B11611B07550009A862E /* DWARFFormValue.cpp in Sources */, + 26D5B11711B07550009A862E /* DWARFLocationDescription.cpp in Sources */, + 26D5B11811B07550009A862E /* DWARFLocationList.cpp in Sources */, + 26D5B11911B07550009A862E /* SymbolFileDWARF.cpp in Sources */, + 26D5B11A11B07550009A862E /* SymbolFileDWARFDebugMap.cpp in Sources */, + 26D5B11B11B07550009A862E /* SymbolFileSymtab.cpp in Sources */, + 26D5B11C11B07550009A862E /* SymbolVendorMacOSX.cpp in Sources */, + 26D5B11D11B07550009A862E /* CommandObjectUnalias.cpp in Sources */, + 26D5B11E11B07550009A862E /* ScriptInterpreter.cpp in Sources */, + 26D5B11F11B07550009A862E /* BreakpointResolverAddress.cpp in Sources */, + 26D5B12011B07550009A862E /* BreakpointResolverFileLine.cpp in Sources */, + 26D5B12111B07550009A862E /* BreakpointResolverName.cpp in Sources */, + 26D5B12211B07550009A862E /* DisassemblerLLVM.cpp in Sources */, + 26D5B12311B07550009A862E /* ThreadPlanRunToAddress.cpp in Sources */, + 26D5B12411B07550009A862E /* ThreadPlanShouldStopHere.cpp in Sources */, + 26D5B12511B07550009A862E /* ThreadPlanStepInRange.cpp in Sources */, + 26D5B12611B07550009A862E /* ThreadPlanStepOverRange.cpp in Sources */, + 26D5B12711B07550009A862E /* CommandObjectLog.cpp in Sources */, + 26D5B12811B07550009A862E /* ValueObjectRegister.cpp in Sources */, + 26D5B12911B07550009A862E /* TimeValue.cpp in Sources */, + 26D5B12A11B07550009A862E /* UUID.cpp in Sources */, + 26D5B12B11B07550009A862E /* ScriptInterpreterNone.cpp in Sources */, + 26D5B12C11B07550009A862E /* CommandObjectCrossref.cpp in Sources */, + 26D5B12D11B07550009A862E /* CommandObjectMultiword.cpp in Sources */, + 26D5B12E11B07550009A862E /* CommandObjectRegexCommand.cpp in Sources */, + 26D5B12F11B07550009A862E /* Symbols.cpp in Sources */, + 26D5B13011B07550009A862E /* Debugger.cpp in Sources */, + 26D5B13111B07550009A862E /* ProcessGDBRemote.cpp in Sources */, + 26D5B13211B07550009A862E /* ProcessGDBRemoteLog.cpp in Sources */, + 26D5B13311B07550009A862E /* ThreadGDBRemote.cpp in Sources */, + 26D5B13411B07550009A862E /* GDBRemoteCommunication.cpp in Sources */, + 26D5B13511B07550009A862E /* GDBRemoteRegisterContext.cpp in Sources */, + 26D5B13611B07550009A862E /* UnixSignals.cpp in Sources */, + 26D5B13711B07550009A862E /* LogChannelDWARF.cpp in Sources */, + 26D5B13811B07550009A862E /* PseudoTerminal.cpp in Sources */, + 26D5B13911B07550009A862E /* LLDBWrapPython.cpp in Sources */, + 26D5B13A11B07550009A862E /* StringList.cpp in Sources */, + 26D5B13B11B07550009A862E /* CommandCompletions.cpp in Sources */, + 26D5B13C11B07550009A862E /* AddressResolver.cpp in Sources */, + 26D5B13D11B07550009A862E /* AddressResolverFileLine.cpp in Sources */, + 26D5B13E11B07550009A862E /* AddressResolverName.cpp in Sources */, + 26D5B13F11B07550009A862E /* ObjectContainerBSDArchive.cpp in Sources */, + 26D5B14011B07550009A862E /* CommandObjectBreakpointCommand.cpp in Sources */, + 26D5B14111B07550009A862E /* InputReader.cpp in Sources */, + 26D5B14211B07550009A862E /* CommandObjectFrame.cpp in Sources */, + 26D5B14311B07550009A862E /* ABISysV_x86_64.cpp in Sources */, + 26D5B14411B07550009A862E /* ABI.cpp in Sources */, + 26D5B14511B07550009A862E /* StringExtractor.cpp in Sources */, + 26D5B14611B07550009A862E /* ThreadPlanStepUntil.cpp in Sources */, + 26D5B14711B07550009A862E /* ThreadPlanCallFunction.cpp in Sources */, + 26D5B14811B07550009A862E /* ClangFunction.cpp in Sources */, + 26D5B14911B07550009A862E /* RecordingMemoryManager.cpp in Sources */, + 26D5B14A11B07550009A862E /* CommandObjectCall.cpp in Sources */, + 26D5B14B11B07550009A862E /* CommandObjectTarget.cpp in Sources */, + 26D5B14C11B07550009A862E /* PathMappingList.cpp in Sources */, + 26D5B14D11B07550009A862E /* StringExtractorGDBRemote.cpp in Sources */, + 26D5B14E11B07550009A862E /* MacOSXLibunwindCallbacks.cpp in Sources */, + 26D5B15011B07550009A862E /* Registers.s in Sources */, + 26D5B15111B07550009A862E /* unw_getcontext.s in Sources */, + 26D5B15211B07550009A862E /* LibUnwindRegisterContext.cpp in Sources */, + 26D5B15311B07550009A862E /* ABIMacOSX_i386.cpp in Sources */, + 26D5B15411B07550009A862E /* ObjCTrampolineHandler.cpp in Sources */, + 26D5B15511B07550009A862E /* Baton.cpp in Sources */, + 26D5B15611B07550009A862E /* CommandObjectArgs.cpp in Sources */, + 26D5B15711B07550009A862E /* ThreadPlanStepThroughObjCTrampoline.cpp in Sources */, + 26D5B15911B07550009A862E /* UnwindLibUnwind.cpp in Sources */, + 26D5B15A11B07550009A862E /* UnwindMacOSXFrameBackchain.cpp in Sources */, + 26D5B15B11B07550009A862E /* RegisterContextMacOSXFrameBackchain.cpp in Sources */, + 26D5B15C11B07550009A862E /* ObjCObjectPrinter.cpp in Sources */, + 26680324116005D9008E1FE4 /* SBThread.cpp in Sources */, + 26680326116005DB008E1FE4 /* SBTarget.cpp in Sources */, + 26680327116005DC008E1FE4 /* SBSourceManager.cpp in Sources */, + 26680328116005DE008E1FE4 /* SBProcess.cpp in Sources */, + 2668032A116005E0008E1FE4 /* SBListener.cpp in Sources */, + 2668032C116005E2008E1FE4 /* SBFrame.cpp in Sources */, + 2668032D116005E3008E1FE4 /* SBFileSpec.cpp in Sources */, + 2668032E116005E5008E1FE4 /* SBEvent.cpp in Sources */, + 2668032F116005E6008E1FE4 /* SBError.cpp in Sources */, + 26680330116005E7008E1FE4 /* SBDebugger.cpp in Sources */, + 26680331116005E9008E1FE4 /* SBCommunication.cpp in Sources */, + 26680332116005EA008E1FE4 /* SBCommandReturnObject.cpp in Sources */, + 26680333116005EC008E1FE4 /* SBCommandInterpreter.cpp in Sources */, + 26680334116005ED008E1FE4 /* SBCommandContext.cpp in Sources */, + 26680335116005EE008E1FE4 /* SBBroadcaster.cpp in Sources */, + 26680336116005EF008E1FE4 /* SBBreakpointLocation.cpp in Sources */, + 26680337116005F1008E1FE4 /* SBBreakpoint.cpp in Sources */, + 26DE204511618ADA00A093E2 /* SBAddress.cpp in Sources */, + 26DE204711618AED00A093E2 /* SBSymbolContext.cpp in Sources */, + 26DE204D11618E7A00A093E2 /* SBModule.cpp in Sources */, + 26DE205D1161901400A093E2 /* SBFunction.cpp in Sources */, + 26DE205F1161901B00A093E2 /* SBCompileUnit.cpp in Sources */, + 26DE20611161902700A093E2 /* SBBlock.cpp in Sources */, + 26DE20631161904200A093E2 /* SBLineEntry.cpp in Sources */, + 26DE20651161904E00A093E2 /* SBSymbol.cpp in Sources */, + 9A19A6B01163BBB300E0D453 /* SBValue.cpp in Sources */, + 261744781168585B005ADD65 /* SBType.cpp in Sources */, + 9A35758E116CFE0F00E8ED2F /* SBValueList.cpp in Sources */, + 9A357673116E7B6400E8ED2F /* SBStringList.cpp in Sources */, + 9A3576AA116E9AC700E8ED2F /* SBHostOS.cpp in Sources */, + 9AC703AF117675410086C050 /* SBInstruction.cpp in Sources */, + 9AC703B1117675490086C050 /* SBInstructionList.cpp in Sources */, + 9AA69DB1118A024600D753A0 /* SBInputReader.cpp in Sources */, + 26D5B18E11B07979009A862E /* libuwind.cxx in Sources */, + 49F1A74611B3388F003ED505 /* ClangExpressionDeclMap.cpp in Sources */, + 49D7072911B5AD11001AD875 /* ClangASTSource.cpp in Sources */, + 4CA9637B11B6E99A00780E28 /* CommandObjectApropos.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 26F5C26710F3D9A4009D5894 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 26F5C27710F3D9E4009D5894 /* Driver.cpp in Sources */, + 26F5C27810F3D9E4009D5894 /* IOChannel.cpp in Sources */, + 9AA69DA61188F52100D753A0 /* PseudoTerminal.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 262CFC7211A450CB00946C6C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = debugserver; + targetProxy = 262CFC7111A450CB00946C6C /* PBXContainerItemProxy */; + }; + 266803621160110D008E1FE4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 26680206115FD0ED008E1FE4 /* LLDB */; + targetProxy = 266803611160110D008E1FE4 /* PBXContainerItemProxy */; + }; + 26CE0610115C438C0022F371 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "lldb-debugserver"; + targetProxy = 26CE060F115C438C0022F371 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91F008733DB70010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + x86_64, + i386, + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + __STDC_CONSTANT_MACROS, + __STDC_LIMIT_MACROS, + LLDB_CONFIGURATION_DEBUG, + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + VALID_ARCHS = "x86_64 i386"; + }; + name = Debug; + }; + 1DEB91F108733DB70010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + x86_64, + i386, + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + __STDC_CONSTANT_MACROS, + __STDC_LIMIT_MACROS, + LLDB_CONFIGURATION_RELEASE, + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + VALID_ARCHS = "x86_64 i386"; + }; + name = Release; + }; + 26680209115FD0ED008E1FE4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 24; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 24; + EXPORTED_SYMBOLS_FILE = "resources/lldb-framework-exports"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", + ); + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_GC = supported; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_VERSION = 4.2; + HEADER_SEARCH_PATHS = /usr/include/python2.6; + INFOPLIST_FILE = "resources/LLDB-Info.plist"; + INSTALL_PATH = /Developer/Library/PrivateFrameworks; + LD_DYLIB_INSTALL_NAME = "@rpath/LLDB.framework/Versions/A/LLDB"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LLVM_BUILD_DIR)", + ); + LLVM_BUILD_DIR = "$(SRCROOT)/llvm"; + LLVM_CONFIGURATION = Debug; + OTHER_CFLAGS = ( + "-DFOR_DYLD=0", + "-DSUPPORT_REMOTE_UNWINDING", + ); + OTHER_CPLUSPLUSFLAGS = ( + "-Wglobal-constructors", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-lllvmclang", + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = LLDB; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include $(SRCROOT)/source/Host/macosx/cfcpp $(SRCROOT)/llvm/include $(SRCROOT)/llvm/tools/clang/include $(LLVM_BUILD_DIR)/llvm/include $(LLVM_BUILD_DIR)/llvm/tools/clang/include"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 2668020A115FD0ED008E1FE4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 24; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 24; + EXPORTED_SYMBOLS_FILE = "resources/lldb-framework-exports"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", + ); + FRAMEWORK_VERSION = A; + GCC_ENABLE_OBJC_GC = supported; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.2; + HEADER_SEARCH_PATHS = /usr/include/python2.6; + INFOPLIST_FILE = "resources/LLDB-Info.plist"; + INSTALL_PATH = /Developer/Library/PrivateFrameworks; + LD_DYLIB_INSTALL_NAME = "@rpath/LLDB.framework/Versions/A/LLDB"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LLVM_BUILD_DIR)", + ); + LLVM_BUILD_DIR = "$(SRCROOT)/llvm"; + LLVM_CONFIGURATION = Release; + OTHER_CFLAGS = ( + "-DFOR_DYLD=0", + "-DSUPPORT_REMOTE_UNWINDING", + ); + OTHER_CPLUSPLUSFLAGS = ( + "-Wglobal-constructors", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-lllvmclang", + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = LLDB; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include $(SRCROOT)/source/Host/macosx/cfcpp $(SRCROOT)/llvm/include $(SRCROOT)/llvm/tools/clang/include $(LLVM_BUILD_DIR)/llvm/include $(LLVM_BUILD_DIR)/llvm/tools/clang/include"; + VERSIONING_SYSTEM = "apple-generic"; + ZERO_LINK = NO; + }; + name = Release; + }; + 268A89B211963ECA00D953EB /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + x86_64, + i386, + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + __STDC_CONSTANT_MACROS, + __STDC_LIMIT_MACROS, + LLDB_CONFIGURATION_BUILD_AND_INTEGRATION, + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + VALID_ARCHS = "x86_64 i386"; + }; + name = BuildAndIntegration; + }; + 268A89B311963ECA00D953EB /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 24; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", + ); + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_MODEL_TUNING = G5; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.2; + INFOPLIST_FILE = "lldb-Info.plist"; + INSTALL_PATH = /Developer/usr/bin; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/tools/driver/lldb-Info.plist", + "-Wl,-rpath,@loader_path/../../Library/PrivateFrameworks/", + ); + PREBINDING = NO; + PRODUCT_NAME = lldb; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include/lldb/Utility"; + VERSIONING_SYSTEM = "apple-generic"; + ZERO_LINK = NO; + }; + name = BuildAndIntegration; + }; + 268A89B511963ECA00D953EB /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 24; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 24; + EXPORTED_SYMBOLS_FILE = "resources/lldb-framework-exports"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", + ); + FRAMEWORK_VERSION = A; + GCC_ENABLE_OBJC_GC = supported; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.2; + HEADER_SEARCH_PATHS = /usr/include/python2.6; + INFOPLIST_FILE = "resources/LLDB-Info.plist"; + INSTALL_PATH = /Developer/Library/PrivateFrameworks; + LD_DYLIB_INSTALL_NAME = "@rpath/LLDB.framework/Versions/A/LLDB"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LLVM_BUILD_DIR)", + ); + LLVM_BUILD_DIR = "$(DERIVED_FILE_DIR)/llvm.build"; + LLVM_CONFIGURATION = Release; + OTHER_CFLAGS = ( + "-DFOR_DYLD=0", + "-DSUPPORT_REMOTE_UNWINDING", + ); + OTHER_CPLUSPLUSFLAGS = ( + "-Wglobal-constructors", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-lllvmclang", + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = LLDB; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include $(SRCROOT)/source/Host/macosx/cfcpp $(SRCROOT)/llvm/include $(SRCROOT)/llvm/tools/clang/include $(LLVM_BUILD_DIR)/llvm/include $(LLVM_BUILD_DIR)/llvm/tools/clang/include"; + VERSIONING_SYSTEM = "apple-generic"; + ZERO_LINK = NO; + }; + name = BuildAndIntegration; + }; + 26F5C26C10F3D9A5009D5894 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = lldb_codesign; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 24; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.2; + INFOPLIST_FILE = "lldb-Info.plist"; + INSTALL_PATH = /Developer/usr/bin; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/tools/driver/lldb-Info.plist", + "-Wl,-rpath,@loader_path/", + ); + PREBINDING = NO; + PRODUCT_NAME = lldb; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include/lldb/Utility"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 26F5C26D10F3D9A5009D5894 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + x86_64, + i386, + ); + CODE_SIGN_IDENTITY = lldb_codesign; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 24; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", + ); + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_MODEL_TUNING = G5; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = 4.2; + INFOPLIST_FILE = "lldb-Info.plist"; + INSTALL_PATH = /Developer/usr/bin; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/tools/driver/lldb-Info.plist", + "-Wl,-rpath,@loader_path/../../Library/PrivateFrameworks/", + ); + PREBINDING = NO; + PRODUCT_NAME = lldb; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include/lldb/Utility"; + VERSIONING_SYSTEM = "apple-generic"; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "lldb" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91F008733DB70010E9CD /* Debug */, + 1DEB91F108733DB70010E9CD /* Release */, + 268A89B211963ECA00D953EB /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; + 2668020B115FD0EE008E1FE4 /* Build configuration list for PBXNativeTarget "LLDB" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26680209115FD0ED008E1FE4 /* Debug */, + 2668020A115FD0ED008E1FE4 /* Release */, + 268A89B511963ECA00D953EB /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; + 26F5C26E10F3D9C5009D5894 /* Build configuration list for PBXNativeTarget "lldb-tool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26F5C26C10F3D9A5009D5894 /* Debug */, + 26F5C26D10F3D9A5009D5894 /* Release */, + 268A89B311963ECA00D953EB /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/lldb/lldb.xcworkspace/contents.xcworkspacedata b/lldb/lldb.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..8995b415fd4a --- /dev/null +++ b/lldb/lldb.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/lldb/resources/LLDB-Info.plist b/lldb/resources/LLDB-Info.plist new file mode 100644 index 000000000000..922b8c169ccc --- /dev/null +++ b/lldb/resources/LLDB-Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.apple.${PRODUCT_NAME:rfc1034identifier}.framework + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 24 + CFBundleName + ${EXECUTABLE_NAME} + + diff --git a/lldb/resources/lldb-framework-exports b/lldb/resources/lldb-framework-exports new file mode 100644 index 000000000000..174c15940569 --- /dev/null +++ b/lldb/resources/lldb-framework-exports @@ -0,0 +1,3 @@ +__ZN4lldb* +__ZNK4lldb* +_init_lldb diff --git a/lldb/scripts/Python/build-swig-Python.sh b/lldb/scripts/Python/build-swig-Python.sh new file mode 100755 index 000000000000..9dc4b991c4ad --- /dev/null +++ b/lldb/scripts/Python/build-swig-Python.sh @@ -0,0 +1,127 @@ +#!/bin/sh + +# build-swig-Python.sh + + +debug_flag=$1 + +if [ -n "$debug_flag" -a "$debug_flag" == "-debug" ] +then + Debug=1 +else + Debug=0 +fi + + +HEADER_FILES="${SRCROOT}/include/lldb/lldb-types.h"\ +" ${SRCROOT}/include/lldb/API/SBAddress.h"\ +" ${SRCROOT}/include/lldb/API/SBBlock.h"\ +" ${SRCROOT}/include/lldb/API/SBBreakpoint.h"\ +" ${SRCROOT}/include/lldb/API/SBBreakpointLocation.h"\ +" ${SRCROOT}/include/lldb/API/SBBroadcaster.h"\ +" ${SRCROOT}/include/lldb/API/SBCommandContext.h"\ +" ${SRCROOT}/include/lldb/API/SBCommandInterpreter.h"\ +" ${SRCROOT}/include/lldb/API/SBCommandReturnObject.h"\ +" ${SRCROOT}/include/lldb/API/SBCompileUnit.h"\ +" ${SRCROOT}/include/lldb/API/SBDebugger.h"\ +" ${SRCROOT}/include/lldb/API/SBError.h"\ +" ${SRCROOT}/include/lldb/API/SBEvent.h"\ +" ${SRCROOT}/include/lldb/API/SBFrame.h"\ +" ${SRCROOT}/include/lldb/API/SBFunction.h"\ +" ${SRCROOT}/include/lldb/API/SBLineEntry.h"\ +" ${SRCROOT}/include/lldb/API/SBListener.h"\ +" ${SRCROOT}/include/lldb/API/SBModule.h"\ +" ${SRCROOT}/include/lldb/API/SBProcess.h"\ +" ${SRCROOT}/include/lldb/API/SBSourceManager.h"\ +" ${SRCROOT}/include/lldb/API/SBStringList.h"\ +" ${SRCROOT}/include/lldb/API/SBSymbol.h"\ +" ${SRCROOT}/include/lldb/API/SBSymbolContext.h"\ +" ${SRCROOT}/include/lldb/API/SBTarget.h"\ +" ${SRCROOT}/include/lldb/API/SBThread.h"\ +" ${SRCROOT}/include/lldb/API/SBType.h"\ +" ${SRCROOT}/include/lldb/API/SBValue.h" + + +if [ $Debug == 1 ] +then + echo "Header files are:" + echo ${HEADER_FILES} +fi + +NeedToUpdate=0 + +swig_output_file=${SCRIPT_INPUT_FILE_1} + +if [ ! -f $swig_output_file ] +then + NeedToUpdate=1 + if [ $Debug == 1 ] + then + echo "Failed to find LLDBWrapPython.cpp" + fi +fi + +if [ $NeedToUpdate == 0 ] +then + for hdrfile in ${HEADER_FILES} + do + if [ $hdrfile -nt $swig_output_file ] + then + NeedToUpdate=1 + if [ $Debug == 1 ] + then + echo "${hdrfile} is newer than ${swig_output_file}" + echo "swig file will need to be re-built." + fi + fi + done +fi + +framework_python_dir="${CONFIGURATION_BUILD_DIR}/LLDB.framework/Versions/A/Resources/Python" + +if [ ! -L "${framework_python_dir}/_lldb.so" ] +then + NeedToUpdate=1 +fi + +if [ ! -f "${framework_python_dir}/lldb.py" ] +then + NeedToUpdate=1 +fi + + +if [ $NeedToUpdate == 0 ] +then + echo "Everything is up-to-date." + exit 0 +else + echo "SWIG needs to be re-run." +fi + + +# Build the SWIG C++ wrapper file for Python. + +swig -c++ -shadow -python -I"${SRCROOT}/include" -I./. -outdir "${CONFIGURATION_BUILD_DIR}" -o "${SCRIPT_INPUT_FILE_1}" "${SCRIPT_INPUT_FILE_0}" + +# Edit the C++ wrapper file that SWIG generated for Python. There are two +# global string replacements needed, which the following script file takes +# care of. It reads in 'LLDBWrapPython.cpp' and generates +# 'LLDBWrapPython.cpp.edited'. + +# The need for this has been eliminated by fixing the namespace qualifiers on return types. +# Leaving this here for now, just in case... +# +#if [ -f "${SRCROOT}/scripts/Python/edit-swig-python-wrapper-file.py" ] +#then +# python "${SRCROOT}/scripts/Python/edit-swig-python-wrapper-file.py" +#fi + +# +# Now that we've got a C++ file we're happy with (hopefully), rename the +# edited file and move it to the appropriate places. +# + +if [ -f "${SCRIPT_INPUT_FILE_1}.edited" ] +then + mv "${SCRIPT_INPUT_FILE_1}.edited" "${SCRIPT_INPUT_FILE_1}" +fi diff --git a/lldb/scripts/Python/edit-swig-python-wrapper-file.py b/lldb/scripts/Python/edit-swig-python-wrapper-file.py new file mode 100644 index 000000000000..a8c432394716 --- /dev/null +++ b/lldb/scripts/Python/edit-swig-python-wrapper-file.py @@ -0,0 +1,58 @@ +# +# edit-swig-python-wrapper-file.py +# +# This script performs some post-processing editing on the C++ file that +# SWIG generates for python, after running on 'lldb.swig'. In +# particular, the types SWIGTYPE_p_SBThread and SWIGTYPE_p_SBTarget are +# being used, when the types that *should* be used are +# SWIGTYPE_p_lldb__SBThread and SWIGTYPE_p_lldb__SBTarget. +# This script goes through the C++ file SWIG generated, reading it in line +# by line and doing a search-and-replace for these strings. +# + + +import os + +full_input_name = os.environ["SCRIPT_INPUT_FILE_1"]; +full_output_name = full_input_name + ".edited" + +try: + f_in = open (full_input_name, 'r') +except IOError: + print "Error: Unable to open file for reading: " + full_input_name +else: + try: + f_out = open (full_output_name, 'w') + except IOError: + print "Error: Unable to open file for writing: " + full_output_name + else: + target_typedef_found = False + thread_typedef_found = False + + try: + line = f_in.readline() + except IOError: + print "Error occurred while reading file." + else: + while line: + # + # + if (line.find ("SWIGTYPE_p_SBTarget")): + if (line.find ("define") < 0): + line = line.replace ("SWIGTYPE_p_SBTarget", + "SWIGTYPE_p_lldb__SBTarget") + if (line.find ("SWIGTYPE_p_SBThread")): + if (line.find ("define") < 0): + line = line.replace ("SWIGTYPE_p_SBThread", + "SWIGTYPE_p_lldb__SBThread") + f_out.write (line) + try: + line = f_in.readline() + except IOError: + print "Error occurred while reading file." + + try: + f_in.close() + f_out.close() + except: + print "Error occurred while closing files" diff --git a/lldb/scripts/Python/finish-swig-Python-LLDB.sh b/lldb/scripts/Python/finish-swig-Python-LLDB.sh new file mode 100755 index 000000000000..293a8b18c66b --- /dev/null +++ b/lldb/scripts/Python/finish-swig-Python-LLDB.sh @@ -0,0 +1,45 @@ +#! /bin/sh + +# finish-swig-Python.sh +# +# For the Python script interpreter (external to liblldb) to be able to import +# and use the lldb module, there must be a "_lldb.so" in the framework +# resources directory. Here we make a symlink called "_lldb.so" that just +# points to the executable in the LLDB.framework and copy over the needed +# .py files. + +if [ ! -d "${TARGET_BUILD_DIR}/LLDB.framework" ] +then + echo "Error: Unable to find LLDB.framework" >&2 + exit 1 +fi + +# Make the Python directory down in the framework if it doesn't already exist +framework_python_dir="${TARGET_BUILD_DIR}/LLDB.framework/Versions/A/Resources/Python" +if [ ! -d "${framework_python_dir}" ] +then + mkdir -p "${framework_python_dir}" +fi + +# Make the symlink that the script bridge for Python will need in the Python +# framework directory +if [ ! -L "${framework_python_dir}/_lldb.so" ] +then + cd "${framework_python_dir}" + ln -s "../../LLDB" _lldb.so +fi + +# Copy the python module (lldb.py) that was generated by SWIG +# over to the framework Python directory +if [ -f "${CONFIGURATION_BUILD_DIR}/lldb.py" ] +then + cp "${CONFIGURATION_BUILD_DIR}/lldb.py" "${framework_python_dir}" +fi + +# Copy the embedded interpreter script over to the framework Python directory +if [ -f "${SRCROOT}/source/Interpreter/embedded_interpreter.py" ] +then + cp "${SRCROOT}/source/Interpreter/embedded_interpreter.py" "${framework_python_dir}" +fi + +exit 0 diff --git a/lldb/scripts/build-llvm.pl b/lldb/scripts/build-llvm.pl new file mode 100644 index 000000000000..3adfc11996f7 --- /dev/null +++ b/lldb/scripts/build-llvm.pl @@ -0,0 +1,409 @@ +#!/usr/bin/perl + +# This script will take a number ($ENV{SCRIPT_INPUT_FILE_COUNT}) of static archive files +# and pull them apart into object files. These object files will be placed in a directory +# named the same as the archive itself without the extension. Each object file will then +# get renamed to start with the archive name and a '-' character (for archive.a(object.o) +# the object file would becomde archive-object.o. Then all object files are re-made into +# a single static library. This can help avoid name collisions when different archive +# files might contain object files with the same name. + +use strict; +use File::Basename; +use File::Glob ':glob'; +use List::Util qw[min max]; + +our $llvm_dstroot = $ENV{SCRIPT_INPUT_FILE_0}; + +our $libedis_outfile = $ENV{SCRIPT_OUTPUT_FILE_0}; +our ($libedis_basename, $libedis_dirname) = fileparse ($libedis_outfile); +our @libedis_slices; # Skinny mach-o slices for libEnhancedDisassembly.dylib + +our $llvm_clang_outfile = $ENV{SCRIPT_OUTPUT_FILE_1}; +our ($llvm_clang_basename, $llvm_clang_dirname) = fileparse ($llvm_clang_outfile); +our @llvm_clang_slices; # paths to the single architecture static libraries (archives) + +our $llvm_configuration = $ENV{LLVM_CONFIGURATION}; + +our $llvm_revision = "'{2010-06-01T13:00}'"; +our $llvm_source_dir = "$ENV{SRCROOT}"; +our $cc = "$ENV{DEVELOPER_BIN_DIR}/gcc-4.2"; +our $cxx = "$ENV{DEVELOPER_BIN_DIR}/g++-4.2"; +our @archs = split (/\s+/, $ENV{ARCHS}); + +our @archive_files = ( + "$llvm_configuration/lib/libplugin_llvmc_Base.a", + "$llvm_configuration/lib/libplugin_llvmc_Clang.a", + "$llvm_configuration/lib/libclangAnalysis.a", + "$llvm_configuration/lib/libclangAST.a", + "$llvm_configuration/lib/libclangBasic.a", + "$llvm_configuration/lib/libclangCodeGen.a", + "$llvm_configuration/lib/libclangFrontend.a", + "$llvm_configuration/lib/libclangDriver.a", + "$llvm_configuration/lib/libclangIndex.a", + "$llvm_configuration/lib/libclangLex.a", + "$llvm_configuration/lib/libclangRewrite.a", + "$llvm_configuration/lib/libclangParse.a", + "$llvm_configuration/lib/libclangSema.a", + "$llvm_configuration/lib/libCompilerDriver.a", + "$llvm_configuration/lib/libEnhancedDisassembly.a", + "$llvm_configuration/lib/libLLVMAnalysis.a", + "$llvm_configuration/lib/libLLVMArchive.a", + "$llvm_configuration/lib/libLLVMARMAsmParser.a", + "$llvm_configuration/lib/libLLVMARMAsmPrinter.a", + "$llvm_configuration/lib/libLLVMARMCodeGen.a", + "$llvm_configuration/lib/libLLVMARMDisassembler.a", + "$llvm_configuration/lib/libLLVMARMInfo.a", + "$llvm_configuration/lib/libLLVMAsmParser.a", + "$llvm_configuration/lib/libLLVMAsmPrinter.a", + "$llvm_configuration/lib/libLLVMBitReader.a", + "$llvm_configuration/lib/libLLVMBitWriter.a", + "$llvm_configuration/lib/libLLVMCodeGen.a", + "$llvm_configuration/lib/libLLVMCore.a", + "$llvm_configuration/lib/libLLVMExecutionEngine.a", + "$llvm_configuration/lib/libLLVMInstCombine.a", + "$llvm_configuration/lib/libLLVMInstrumentation.a", + "$llvm_configuration/lib/libLLVMipa.a", + "$llvm_configuration/lib/libLLVMInterpreter.a", + "$llvm_configuration/lib/libLLVMipo.a", + "$llvm_configuration/lib/libLLVMJIT.a", + "$llvm_configuration/lib/libLLVMLinker.a", + "$llvm_configuration/lib/libLLVMMC.a", + "$llvm_configuration/lib/libLLVMMCParser.a", + "$llvm_configuration/lib/libLLVMScalarOpts.a", + "$llvm_configuration/lib/libLLVMSelectionDAG.a", + "$llvm_configuration/lib/libLLVMSupport.a", + "$llvm_configuration/lib/libLLVMSystem.a", + "$llvm_configuration/lib/libLLVMTarget.a", + "$llvm_configuration/lib/libLLVMTransformUtils.a", + "$llvm_configuration/lib/libLLVMX86AsmParser.a", + "$llvm_configuration/lib/libLLVMX86AsmPrinter.a", + "$llvm_configuration/lib/libLLVMX86CodeGen.a", + "$llvm_configuration/lib/libLLVMX86Disassembler.a", + "$llvm_configuration/lib/libLLVMX86Info.a", + "$llvm_configuration/lib/libclangChecker.a" +); + +if (-l $llvm_dstroot) +{ + print "Using standard LLVM build directory...\n"; + # LLVM in the "lldb" root is a symlink which indicates we are using a + # standard LLVM build directory where everything is built into the + # same folder + create_single_llvm_arhive_for_arch ($llvm_dstroot, 0); + my $llvm_dstroot_archive = "$llvm_dstroot/$llvm_clang_basename"; + push @llvm_clang_slices, $llvm_dstroot_archive; + create_dstroot_file ($llvm_clang_basename, $llvm_clang_dirname, \@llvm_clang_slices, $llvm_clang_basename); + my $llvm_dstroot_edis = "$llvm_dstroot/$llvm_configuration/lib/libEnhancedDisassembly.dylib"; + if (-f $llvm_dstroot_edis) + { + push @libedis_slices, $llvm_dstroot_edis; + create_dstroot_file ($libedis_basename, $libedis_dirname, \@libedis_slices, $libedis_basename); + } + exit 0; +} + + +if ($ENV{CONFIGURATION} eq "Debug" or $ENV{CONFIGURATION} eq "Release") +{ + # Check for an old llvm source install (not the minimal zip based + # install by looking for a .svn file in the llvm directory + chomp(my $llvm_zip_md5 = `md5 -q $ENV{SRCROOT}/llvm.zip`); + my $llvm_zip_md5_file = "$ENV{SRCROOT}/llvm/$llvm_zip_md5"; + if (!-e "$llvm_zip_md5_file") + { + print "Updating LLVM to use checkpoint from: '$ENV{SRCROOT}/llvm.zip'...\n"; + if (-d "$ENV{SRCROOT}/llvm") + { + do_command ("cd '$ENV{SRCROOT}' && rm -rf llvm", "removing old llvm repository", 1); + } + do_command ("cd '$ENV{SRCROOT}' && unzip -q llvm.zip && touch '$llvm_zip_md5_file'", "expanding llvm.zip", 1); + } + + # We use the stuff in "lldb/llvm" for non B&I builds + if (!-e $libedis_outfile) + { + print "Copying '$ENV{SRCROOT}/llvm/$libedis_basename' to '$libedis_outfile'...\n"; + do_command ("cp '$ENV{SRCROOT}/llvm/$libedis_basename' '$libedis_outfile'", "copying libedis", 1); + } + exit 0; +} + +# If our output file already exists then we need not generate it again. +if (-e $llvm_clang_outfile and -e $libedis_outfile) +{ + exit 0; +} + + +# Get our options + +our $debug = 1; + +sub parallel_guess +{ + my $cpus = `sysctl -n hw.availcpu`; + chomp ($cpus); + my $memsize = `sysctl -n hw.memsize`; + chomp ($memsize); + my $max_cpus_by_memory = int($memsize / (750 * 1024 * 1024)); + return min($max_cpus_by_memory, $cpus); +} +sub build_llvm +{ + #my $extra_svn_options = $debug ? "" : "--quiet"; + my $svn_options = "--quiet --revision $llvm_revision"; + if (-d "$llvm_source_dir/llvm") + { + print "Using existing llvm sources in: '$llvm_source_dir/llvm'\n"; + # print "Updating llvm to revision $llvm_revision\n"; + # do_command ("cd '$llvm_source_dir/llvm' && svn update $svn_options", "updating llvm from repository", 1); + # print "Updating clang to revision $llvm_revision\n"; + # do_command ("cd '$llvm_source_dir/llvm/tools/clang' && svn update $svn_options", "updating clang from repository", 1); + } + else + { + print "Checking out llvm sources from revision $llvm_revision...\n"; + do_command ("cd '$llvm_source_dir' && svn co $svn_options http://llvm.org/svn/llvm-project/llvm/trunk llvm", "checking out llvm from repository", 1); + print "Checking out clang sources from revision $llvm_revision...\n"; + do_command ("cd '$llvm_source_dir/llvm/tools' && svn co $svn_options http://llvm.org/svn/llvm-project/cfe/trunk clang", "checking out clang from repository", 1); + print "Removing the llvm/test directory...\n"; + do_command ("cd '$llvm_source_dir' && rm -rf llvm/test", "removing test directory", 1); + } + + # Make the llvm build directory + my $arch_idx = 0; + foreach my $arch (@archs) + { + my $llvm_dstroot_arch = "${llvm_dstroot}/${arch}"; + + # if the arch destination root exists we have already built it + my $do_configure = 0; + my $do_make = 0; + + my $llvm_dstroot_arch_archive = "$llvm_dstroot_arch/$llvm_clang_basename"; + print "LLVM architecture root for ${arch} exists at '$llvm_dstroot_arch'..."; + if (-e $llvm_dstroot_arch) + { + print "YES\n"; + $do_configure = !-e "$llvm_dstroot_arch/config.log"; + + # dstroot for llvm build exists, make sure all .a files are built + for my $llvm_lib (@archive_files) + { + if (!-e "$llvm_dstroot_arch/$llvm_lib") + { + print "missing archive: '$llvm_dstroot_arch/$llvm_lib'\n"; + $do_make = 1; + } + } + if (!-e $llvm_dstroot_arch_archive) + { + $do_make = 1; + } + } + else + { + print "NO\n"; + do_command ("mkdir -p '$llvm_dstroot_arch'", "making llvm build directory '$llvm_dstroot_arch'", 1); + $do_configure = 1; + $do_make = 1; + } + + # If this is the first architecture, then make a symbolic link + # for any header files that get generated. + if ($arch_idx == 0) + { + if (!-l "$llvm_dstroot/llvm") + { + do_command ("cd $llvm_dstroot && ln -s './${arch}' llvm"); + } + } + + if ($do_configure) + { + # Build llvm and clang + print "Configuring clang ($arch) in '$llvm_dstroot_arch'...\n"; + my $lldb_configuration_options = ''; + $llvm_configuration eq 'Release' and $lldb_configuration_options .= '--enable-optimized'; + do_command ("cd '$llvm_dstroot_arch' && '$llvm_source_dir/llvm/configure' $lldb_configuration_options --enable-targets=x86,x86_64,arm --build=$arch-apple-darwin10 CC=\"$cc -arch $arch\" CXX=\"$cxx -arch $arch\"", + "configuring llvm build", 1); + } + + if ($do_make) + { + # Build llvm and clang + my $num_cpus = parallel_guess(); + print "Building clang using $num_cpus cpus ($arch)...\n"; + do_command ("cd '$llvm_dstroot_arch' && make -j$num_cpus clang-only VERBOSE=1 PROJECT_NAME='llvm'", "making llvm and clang", 1); + do_command ("cd '$llvm_dstroot_arch' && make -j$num_cpus tools-only VERBOSE=1 PROJECT_NAME='llvm' EDIS_VERSION=1", "making libedis", 1); + # Combine all .o files from a bunch of static libraries from llvm + # and clang into a single .a file. + create_single_llvm_arhive_for_arch ($llvm_dstroot_arch, 1); + } + + -f "$llvm_dstroot_arch_archive" and push @llvm_clang_slices, "$llvm_dstroot_arch_archive"; + -f "$llvm_dstroot_arch/$llvm_configuration/lib/libEnhancedDisassembly.dylib" and push @libedis_slices, "$llvm_dstroot_arch/$llvm_configuration/lib/libEnhancedDisassembly.dylib"; + ++$arch_idx; + } + + # Combine all skinny slices of the LLVM/Clang combined archive + create_dstroot_file ($llvm_clang_basename, $llvm_clang_dirname, \@llvm_clang_slices, $llvm_clang_basename); + + if (scalar(@libedis_slices)) + { + # Combine all skinny slices of the libedis in SYMROOT + create_dstroot_file ($libedis_basename, $libedis_dirname, \@libedis_slices, $libedis_basename); + + # Make dSYM for libedis in SYMROOT + do_command ("cd '$libedis_dirname' && dsymutil $libedis_basename", "making libedis dSYM", 1); + + # strip debug symbols from libedis and copy into DSTROOT + -d "$ENV{DSTROOT}/Developer/usr/lib" or do_command ("mkdir -p '$ENV{DSTROOT}/Developer/usr/lib'", "Making directory '$ENV{DSTROOT}/Developer/usr/lib'", 1); + do_command ("cd '$libedis_dirname' && strip -Sx -o '$ENV{DSTROOT}/Developer/usr/lib/$libedis_basename' '$libedis_outfile'", "Stripping libedis and copying to DSTROOT", 1); + } +} + +sub create_dstroot_file +{ + my $file = shift; + my $dir = shift; + my $fullpath = "$dir/$file"; # The path to the file to create + my $slice_aref = shift; # Array containing one or more skinny files that will be combined into $fullpath + my $what = shift; # Text describing the $fullpath + + print "create_dstroot_file file = '$file', dir = '$dir', slices = (" . join (', ', @$slice_aref) . ") for what = '$what'\n"; + + if (-d $dir) + { + if (@$slice_aref > 0) + { + print "Creating and installing $what into '$fullpath'...\n"; + my $lipo_command = "lipo -output '$fullpath' -create"; + foreach (@$slice_aref) { $lipo_command .= " '$_'"; } + do_command ($lipo_command, "creating $what universal output file", 1); + } + + + if (!-e $fullpath) + { + # die "error: '$fullpath' is missing\n"; + } + } + else + { + die "error: directory '$dir' doesn't exist to receive file '$file'\n"; + } +} +#---------------------------------------------------------------------- +# quote the path if needed and realpath it if the -r option was +# specified +#---------------------------------------------------------------------- +sub finalize_path +{ + my $path = shift; + # Realpath all paths that don't start with "/" + $path =~ /^[^\/]/ and $path = abs_path($path); + + # Quote the path if asked to, or if there are special shell characters + # in the path name + my $has_double_quotes = $path =~ /["]/; + my $has_single_quotes = $path =~ /[']/; + my $needs_quotes = $path =~ /[ \$\&\*'"]/; + if ($needs_quotes) + { + # escape and double quotes in the path + $has_double_quotes and $path =~ s/"/\\"/g; + $path = "\"$path\""; + } + return $path; +} + +sub do_command +{ + my $cmd = shift; + my $description = @_ ? shift : "command"; + my $die_on_fail = @_ ? shift : undef; + $debug and print "% $cmd\n"; + system ($cmd); + if ($? == -1) + { + $debug and printf ("error: %s failed to execute: $!\n", $description); + $die_on_fail and $? and exit(1); + return $?; + } + elsif ($? & 127) + { + $debug and printf("error: %s child died with signal %d, %s coredump\n", + $description, + ($? & 127), + ($? & 128) ? 'with' : 'without'); + $die_on_fail and $? and exit(1); + return $?; + } + else + { + my $exit = $? >> 8; + if ($exit) + { + $debug and printf("error: %s child exited with value %d\n", $description, $exit); + $die_on_fail and exit(1); + } + return $exit; + } +} + +sub create_single_llvm_arhive_for_arch +{ + my $arch_dstroot = shift; + my $split_into_objects = shift; + my @object_dirs; + my $object_dir; + my $tmp_dir = $arch_dstroot; + my $arch_output_file = "$arch_dstroot/$llvm_clang_basename"; + -e $arch_output_file and return; + my $files = "$arch_dstroot/files.txt"; + open (FILES, ">$files") or die "Can't open $! for writing...\n"; + + for my $path (@archive_files) + { + my $archive_fullpath = finalize_path ("$arch_dstroot/$path"); + if (-e $archive_fullpath) + { + if ($split_into_objects) + { + my ($archive_file, $archive_dir, $archive_ext) = fileparse($archive_fullpath, ('.a')); + + $object_dir = "$tmp_dir/$archive_file"; + push @object_dirs, $object_dir; + + do_command ("cd '$tmp_dir'; mkdir '$archive_file'; cd '$archive_file'; ar -x $archive_fullpath"); + + my @objects = bsd_glob("$object_dir/*.o"); + + foreach my $object (@objects) + { + my ($o_file, $o_dir) = fileparse($object); + my $new_object = "$object_dir/${archive_file}-$o_file"; + print FILES "$new_object\n"; + do_command ("mv '$object' '$new_object'"); + } + } + else + { + # just add the .a files into the file list + print FILES "$archive_fullpath\n"; + } + } + } + close (FILES); + do_command ("libtool -static -o '$arch_output_file' -filelist '$files'"); + + foreach $object_dir (@object_dirs) + { + do_command ("rm -rf '$object_dir'"); + } + do_command ("rm -rf '$files'"); +} + +build_llvm(); diff --git a/lldb/scripts/build-swig-wrapper-classes.sh b/lldb/scripts/build-swig-wrapper-classes.sh new file mode 100755 index 000000000000..3d1cf74344fb --- /dev/null +++ b/lldb/scripts/build-swig-wrapper-classes.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +# build-swig-wrapper-classes.sh +# +# For each scripting language liblldb supports, we need to create the +# appropriate Script Bridge wrapper classes for that language so that +# users can call Script Bridge functions from within the script interpreter. +# +# We use SWIG to help create the appropriate wrapper classes/functions for +# the scripting language. In some cases the file generated by SWIG may +# need some tweaking before it is completely ready to use. + +debug_flag=$1 + +if [ -n "$debug_flag" -a "$debug_flag" == "-debug" ] +then + Debug=1 +else + Debug=0 +fi + +# +# Verify that 'lldb.swig' exists. +# + +if [ ! -f ${SRCROOT}/scripts/lldb.swig ] +then + echo Error: unable to find file 'lldb.swig' >&2 + exit 1 +fi + +if [ $Debug == 1 ] +then + echo "Found lldb.swig file" +fi + +# +# For each scripting language, make sure the build script for that language +# exists, and if so, call it. +# +# For now the only language we support is Python, but we expect this to +# change. + +languages="Python" +cwd=${SRCROOT}/scripts + +for curlang in $languages +do + if [ $Debug == 1 ] + then + echo "Current language is $curlang" + fi + + if [ ! -d "$cwd/$curlang" ] + then + echo "Error: unable to find $curlang script sub-dirctory" >&2 + continue + else + + if [ $Debug == 1 ] + then + echo "Found $curlang sub-directory" + fi + + cd $cwd/$curlang + + filename="./build-swig-${curlang}.sh" + + if [ ! -f $filename ] + then + echo "Error: unable to find swig build script for $curlang: $filename" >&2 + continue + else + + if [ $Debug == 1 ] + then + echo "Found $curlang build script." + echo "Executing $curlang build script..." + fi + + ./build-swig-${curlang}.sh + fi + fi +done + diff --git a/lldb/scripts/checkpoint-llvm.pl b/lldb/scripts/checkpoint-llvm.pl new file mode 100755 index 000000000000..5c1293b0f334 --- /dev/null +++ b/lldb/scripts/checkpoint-llvm.pl @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +# This script should be pointed to a valid llvm.build folder that +# was created using the "build-llvm.pl" shell script. It will create +# a new llvm.zip file that can be checked into the respository +# at lldb/llvm.zip + +use strict; +use Cwd 'abs_path'; +use File::Basename; +use File::Temp qw/ tempfile tempdir /; +our $debug = 1; + + +sub do_command +{ + my $cmd = shift; + my $description = @_ ? shift : "command"; + my $die_on_fail = @_ ? shift : undef; + $debug and print "% $cmd\n"; + system ($cmd); + if ($? == -1) + { + $debug and printf ("error: %s failed to execute: $!\n", $description); + $die_on_fail and $? and exit(1); + return $?; + } + elsif ($? & 127) + { + $debug and printf("error: %s child died with signal %d, %s coredump\n", + $description, + ($? & 127), + ($? & 128) ? 'with' : 'without'); + $die_on_fail and $? and exit(1); + return $?; + } + else + { + my $exit = $? >> 8; + if ($exit) + { + $debug and printf("error: %s child exited with value %d\n", $description, $exit); + $die_on_fail and exit(1); + } + return $exit; + } +} + +if (@ARGV == 4) +{ + my $llvm_source_dir = abs_path(shift @ARGV); # The llvm source that contains full llvm and clang sources + my $llvm_build_dir = abs_path(shift @ARGV); # The llvm build directory that contains headers and + my $lldb_build_dir = abs_path(shift @ARGV); # the build directory that contains the fat libEnhancedDisassembly.dylib + my $llvm_zip_file = abs_path(shift @ARGV); + + printf("LLVM sources : '%s'\n", $llvm_source_dir); + printf("LLVM build : '%s'\n", $llvm_build_dir); + printf("LLDB build : '%s'\n", $lldb_build_dir); + printf("LLVM zip file: '%s'\n", $llvm_zip_file); + + -e $llvm_build_dir or die "LLVM build directory doesn't exist: '$llvm_build_dir': $!\n"; + -l "$llvm_build_dir/llvm" || die "Couldn't find llvm symlink '$llvm_build_dir/llvm': $!\n"; + + my $temp_dir = tempdir( CLEANUP => 1 ); + print "temp dir = '$temp_dir'\n"; + my $llvm_checkpoint_dir = "$temp_dir/llvm"; + mkdir "$llvm_checkpoint_dir" or die "Couldn't make 'llvm' in '$temp_dir'\n"; + + my @rsync_src_dst_paths = + ( + "$llvm_source_dir/include", "$llvm_checkpoint_dir", + "$llvm_source_dir/tools/clang/include", "$llvm_checkpoint_dir/tools/clang", + "$llvm_build_dir/llvm/include", "$llvm_checkpoint_dir", + "$llvm_build_dir/llvm/tools/clang/include", "$llvm_checkpoint_dir/tools/clang", + ); + + while (@rsync_src_dst_paths) + { + my $rsync_src = shift @rsync_src_dst_paths; + my $rsync_dst = shift @rsync_src_dst_paths; + print "rsync_src = '$rsync_src'\n"; + print "rsync_dst = '$rsync_dst'\n"; + if (-e $rsync_src) + { + my ($rsync_dst_file, $rsync_dst_dir) = fileparse ($rsync_dst); + print "rsync_dst_dir = '$rsync_dst_dir'\n"; + -e $rsync_dst_dir or do_command ("mkdir -p '$rsync_dst_dir'"); + do_command ("rsync -amvC --exclude='*.tmp' --exclude='*.txt' --exclude='*.TXT' --exclude='*.td' --exclude='\.dir' --exclude=Makefile '$rsync_src' '$rsync_dst'"); + } + } + + do_command ("cp '$llvm_build_dir/libllvmclang.a' '$llvm_checkpoint_dir'", "Copying libllvmclang.a", 1); + do_command ("cp '$lldb_build_dir/libEnhancedDisassembly.dylib' '$llvm_checkpoint_dir'", "Copying libEnhancedDisassembly.dylib", 1); + do_command ("rm -rf '$llvm_zip_file'", "Removing old llvm checkpoint file '$llvm_zip_file'", 1); + do_command ("(cd '$temp_dir' ; zip -r '$llvm_zip_file' 'llvm')", "Zipping llvm checkpoint directory '$llvm_checkpoint_dir' to '$llvm_zip_file'", 1); +} +else +{ + print "USAGE\n\tcheckpoint-llvm.pl \n\n"; + print "EXAMPLE\n\tcd lldb\n\t./scripts/checkpoint-llvm.pl llvm build/lldb.build/BuildAndIntegration/LLDB.build/DerivedSources/llvm.build build/BuildAndIntegration llvm.zip\n"; +} diff --git a/lldb/scripts/finish-swig-wrapper-classes.sh b/lldb/scripts/finish-swig-wrapper-classes.sh new file mode 100755 index 000000000000..5d9713d06a28 --- /dev/null +++ b/lldb/scripts/finish-swig-wrapper-classes.sh @@ -0,0 +1,71 @@ +#! /bin/sh + +# finish-swig-wrapper-classes.sh +# +# For each scripting language liblldb supports, we need to create the +# appropriate Script Bridge wrapper classes for that language so that +# users can call Script Bridge functions from within the script interpreter. +# +# We use SWIG to create a C++ file containing the appropriate wrapper classes +# and funcitons for each scripting language, before liblldb is built (thus +# the C++ file can be compiled into liblldb. In some cases, additional work +# may need to be done after liblldb has been compiled, to make the scripting +# language stuff fully functional. Any such post-processing is handled through +# the shell scripts called here. + +debug_flag=$1 + +if [ -n "$debug_flag" -a "$debug_flag" == "-debug" ] +then + Debug=1 +else + Debug=0 +fi + + +# +# For each scripting language, see if a post-processing script for that +# language exists, and if so, call it. +# +# For now the only language we support is Python, but we expect this to +# change. + +languages="Python" +cwd=${SRCROOT}/scripts + +for curlang in $languages +do + if [ $Debug == 1 ] + then + echo "Current language is $curlang" + fi + + if [ ! -d "$cwd/$curlang" ] + then + echo "error: unable to find $curlang script sub-dirctory" >&2 + continue + else + + if [ $Debug == 1 ] + then + echo "Found $curlang sub-directory" + fi + + cd $cwd/$curlang + + filename="./finish-swig-${curlang}-${TARGET_NAME}.sh" + + if [ -f $filename ] + then + if [ $Debug == 1 ] + then + echo "Found $curlang post-processing script for ${TARGET_NAME}" + echo "Executing $curlang post-processing script..." + fi + + ./finish-swig-${curlang}-${TARGET_NAME}.sh + fi + fi +done + +exit 0 diff --git a/lldb/scripts/install-lldb.sh b/lldb/scripts/install-lldb.sh new file mode 100755 index 000000000000..0ba4e7c5ee2b --- /dev/null +++ b/lldb/scripts/install-lldb.sh @@ -0,0 +1,59 @@ +#!/bin/sh + + +# This script will install the files from a "Debug" or "Release" build +# directory into the developer folder specified. + +NUM_EXPECTED_ARGS=2 + +PROGRAM=`basename $0` + +if [ $# -ne $NUM_EXPECTED_ARGS ]; then + echo This script will install the files from a 'Debug' or 'Release' build directory into the developer folder specified. + echo "usage: $PROGRAM "; + echo "example: $PROGRAM ./Debug /Developer" + echo "example: $PROGRAM /build/Release /Xcode4" + exit 1; +fi + +BUILD_DIR=$1 +DEVELOPER_DIR=$2 + +if [ -d $BUILD_DIR ]; then + if [ -d $DEVELOPER_DIR ]; then + if [ -e "$BUILD_DIR/debugserver" ]; then + echo Updating "$DEVELOPER_DIR/usr/bin/debugserver" + sudo rm -rf "$DEVELOPER_DIR/usr/bin/debugserver" + sudo cp "$BUILD_DIR/debugserver" "$DEVELOPER_DIR/usr/bin/debugserver" + fi + + if [ -e "$BUILD_DIR/lldb" ]; then + echo Updating "$DEVELOPER_DIR/usr/bin/lldb" + sudo rm -rf "$DEVELOPER_DIR/usr/bin/lldb" + sudo cp "$BUILD_DIR/lldb" "$DEVELOPER_DIR/usr/bin/lldb" + fi + + if [ -e "$BUILD_DIR/libEnhancedDisassembly.dylib" ]; then + echo Updating "$DEVELOPER_DIR/usr/lib/libEnhancedDisassembly.dylib" + sudo rm -rf "$DEVELOPER_DIR/usr/lib/libEnhancedDisassembly.dylib" + sudo cp "$BUILD_DIR/libEnhancedDisassembly.dylib" "$DEVELOPER_DIR/usr/lib/libEnhancedDisassembly.dylib" + fi + + if [ -d "$BUILD_DIR/LLDB.framework" ]; then + echo Updating "$DEVELOPER_DIR/Library/PrivateFrameworks/LLDB.framework" + sudo rm -rf "$DEVELOPER_DIR/Library/PrivateFrameworks/LLDB.framework" + sudo cp -r "$BUILD_DIR/LLDB.framework" "$DEVELOPER_DIR/Library/PrivateFrameworks/LLDB.framework" + elif [ -e "$BUILD_DIR/LLDB.framework" ]; then + echo BUILD_DIR path to LLDB.framework is not a directory: "$BUILD_DIR/LLDB.framework" + exit 2; + fi + + else + echo DEVELOPER_DIR must be a directory: "$DEVELOPER_DIR" + exit 3; + fi + +else + echo BUILD_DIR must be a directory: "$BUILD_DIR" + exit 4; +fi diff --git a/lldb/scripts/lldb.swig b/lldb/scripts/lldb.swig new file mode 100644 index 000000000000..d9fd05b8488b --- /dev/null +++ b/lldb/scripts/lldb.swig @@ -0,0 +1,149 @@ +/* + lldb.swig + + Created by Caroline Tice 1/18/2010 + + This is the input file for SWIG, to create the appropriate C++ wrappers and + functions for various scripting languages, to enable them to call the + liblldb Script Bridge functions. + +*/ + +/* The name of the module to be created. */ + +%module lldb + +%typemap(in) lldb::ReturnStatus { + $1 = (int) $input; +} + +%typemap(freearg) lldb::ReturnStatus { +} + +%typemap(out) lldb::ReturnStatus { + $result = SWIG_From_unsigned_SS_int(static_cast< unsigned int >($1)); +} + +/* Typemap definitions, to allow SWIG to properly handle 'char**' data types. */ + +%typemap(in) char ** { + /* Check if is a list */ + if (PyList_Check($input)) { + int size = PyList_Size($input); + int i = 0; + $1 = (char **) malloc((size+1) * sizeof(char)); + for (i = 0; i < size; i++) { + PyObject *o = PyList_GetItem($input,i); + if (PyString_Check(o)) + $1[i] = PyString_AsString(PyList_GetItem($input,i)); + else { + PyErr_SetString(PyExc_TypeError,"list must contain strings"); + free($1); + return NULL; + } + } + $1[i] = 0; + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + return NULL; + } +} + +%typemap(freearg) char** { + free((char *) $1); +} + +%typemap(out) char** { + int len; + int i; + len = 0; + while ($1[len]) len++; + $result = PyList_New(len); + for (i = 0; i < len; i++) { + PyList_SetItem($result, i, PyString_FromString($1[i])); + } +} + + +/* The liblld header files to be included. */ + +%{ +#include "lldb/lldb-types.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/SBCommandContext.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBFunction.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/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" +using namespace lldb; +using namespace lldb_private; +%} + +/* Various liblldb typedefs that SWIG needs to know about. */ + +%{ +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef int int32_t; +typedef int32_t pid_t; +typedef uint32_t tid_t; +typedef uint64_t addr_t; +%} + +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef int int32_t; +typedef int32_t pid_t; +typedef uint32_t tid_t; +typedef uint64_t addr_t; + + +%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/SBCommandContext.h" +%include "lldb/API/SBCommandInterpreter.h" +%include "lldb/API/SBCommandReturnObject.h" +%include "lldb/API/SBCompileUnit.h" +%include "lldb/API/SBDebugger.h" +%include "lldb/API/SBError.h" +%include "lldb/API/SBEvent.h" +%include "lldb/API/SBFrame.h" +%include "lldb/API/SBFunction.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/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/lldb-types.h" + + diff --git a/lldb/scripts/sed-sources b/lldb/scripts/sed-sources new file mode 100755 index 000000000000..f678ee275512 --- /dev/null +++ b/lldb/scripts/sed-sources @@ -0,0 +1,252 @@ +#!/usr/bin/perl + +use strict; +use File::Find; +use File::Temp qw/ tempfile tempdir /; +use Getopt::Std; +use Pod::Usage; +use Text::Tabs; + +=head1 NAME + +B -- Performs multiple sed commands on files with the ability to expand or unexpand tabs. + +=head1 SYNOPSIS + +B [options] [file dir ...] + +=head1 DESCRIPTION + +Performs multiple sed commands (modify builtin %seds hash) on source files +or any sources in directories. If no arguments are given, STDIN will be used +as the source. If source files or directories are specified as arguments, +all files will be transformed and overwritten with new versions. Use the B<-p> +option to preview changes to STDOUT, or use the B<-b> option to make a backup +or the original files. + +=head1 OPTIONS + +=over + +=item B<-b> + +Backup original source file by appending ".bak" before overwriting with the +newly transformed file. + +=item B<-g> + +Display verbose debug logging. + +=item B<-e> + +Expand tabs to spaces (in addition to doing sed substitutions). + +=item B<-u> + +Unexpand spaces to tabs (in addition to doing sed substitutions). + +=item B<-p> + +Preview changes to STDOUT without modifying original source files. + +=item B<-r> + +Skip variants when doing multiple files (no _profile or _debug variants). + +=item B<-t N> + +Set the number of spaces per tab (default is 4) to use when expanding or +unexpanding. + +=back + +=head1 EXAMPLES + +# Recursively process all source files in the current working directory +# and and subdirectories and also expand tabs to spaces. All source files +# will be overwritten with the newly transformed source files. + +% sed-sources -e $cwd + +# Recursively process all source files in the current working directory +# and and subdirectories and also unexpand spaces to tabs and preview the +# results to STDOUT + +% sed-sources -p -u $cwd + +# Same as above except use 8 spaces per tab. + +% sed-sources -p -u -t8 $cwd + +=cut + + +our $opt_b = 0; # Backup original file? +our $opt_g = 0; # Verbose debug output? +our $opt_e = 0; # Expand tabs to spaces? +our $opt_h = 0; # Show help? +our $opt_m = 0; # Show help manpage style? +our $opt_p = 0; # Preview changes to STDOUT? +our $opt_t = 4; # Number of spaces per tab? +our $opt_u = 0; # Unexpand spaces to tabs? +getopts('eghmpt:u'); + +$opt_m and show_manpage(); +$opt_h and help(); + +our %seds = ( + '\s+$' => "\n", # Get rid of spaces at the end of a line + '^\s+$' => "\n", # Get rid spaces on lines that are all spaces + '^\s*//\s*[Cc]opyright.*$' => '//', # Get rid of all copyright lines +); + + +sub show_manpage { exit pod2usage( verbose => 2 ); }; +sub help { exit pod2usage( verbose => 3, noperldoc => 1 ); }; + + +#---------------------------------------------------------------------- +# process_opened_file_handle +#---------------------------------------------------------------------- +sub process_opened_file_handle +{ + my $in_fh = shift; + my $out_fh = shift; + + # Set the number of spaces per tab for expand/unexpand + $tabstop = $opt_t; + + while (my $line = <$in_fh>) + { + foreach my $key (keys %seds) + { + my $value = $seds{"$key"}; + $line =~ s/$key/$value/g; + } + if ($opt_e) { + print $out_fh expand $line; + } elsif ($opt_u) { + print $out_fh unexpand $line; + } else { + print $out_fh $line; + } + } +} + +#---------------------------------------------------------------------- +# process_file +#---------------------------------------------------------------------- +sub process_file +{ + my $in_path = shift; + if (-T $in_path) + { + my $out_fh; + my $out_path; + if ($opt_p) + { + # Preview to STDOUT + $out_fh = *STDOUT; + print "#---------------------------------------------------------------------- \n"; + print "# BEGIN: '$in_path'\n"; + print "#---------------------------------------------------------------------- \n"; + } + else + { + ($out_fh, $out_path) = tempfile(); + $opt_g and print "temporary for '$in_path' is '$out_path'\n"; + } + open (IN, "<$in_path") or die "error: can't open '$in_path' for reading: $!"; + process_opened_file_handle (*IN, $out_fh); + + + # Close our input file + close (IN); + + if ($opt_p) + { + print "#---------------------------------------------------------------------- \n"; + print "# END: '$in_path'\n"; + print "#---------------------------------------------------------------------- \n"; + print "\n\n"; + } + else + { + # Close the output file if it wasn't STDOUT + close ($out_fh); + + # Backup file if requested + if ($opt_b) + { + my $backup_command = "cp '$in_path' '$in_path.bak'"; + $opt_g and print "\% $backup_command\n"; + system ($backup_command); + } + + # Copy temp file over original + my $copy_command = "cp '$out_path' '$in_path'"; + $opt_g and print "\% $copy_command\n"; + system ($copy_command); + } + } +} + +our @valid_extensions = ( "h", "cpp", "c", "m", "mm" ); + +#---------------------------------------------------------------------- +# find_callback +#---------------------------------------------------------------------- +sub find_callback +{ + my $file = $_; + my $fullpath = $File::Find::name; + + foreach my $ext (@valid_extensions) + { + my $ext_regex = "\\.$ext\$"; + if ($fullpath =~ /$ext_regex/i) + { + print "processing: '$fullpath'\n"; + process_file ($fullpath); + return; + } + } + print " ignoring: '$fullpath'\n"; +} + + +#---------------------------------------------------------------------- +# main +#---------------------------------------------------------------------- +sub main +{ + if (@ARGV == 0) + { + # no args, take from STDIN and put to STDOUT + process_opened_file_handle (*STDIN, *STDOUT); + } + else + { + # Got args, any files we run into parse them, any directories + # we run into, search them for files + my $path; + foreach $path (@ARGV) + { + if (-f $path) + { + print "processing: '$path'\n"; + process_file ($path); + } + else + { + print " searching: '$path'\n"; + find(\&find_callback, $path); + } + } + } +} + + + +# call the main function +main(); \ No newline at end of file diff --git a/lldb/source/API/SBAddress.cpp b/lldb/source/API/SBAddress.cpp new file mode 100644 index 000000000000..482ecec1ce69 --- /dev/null +++ b/lldb/source/API/SBAddress.cpp @@ -0,0 +1,118 @@ +//===-- 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/Core/Address.h" + +using namespace lldb; + + +SBAddress::SBAddress () : + m_lldb_object_ap () +{ +} + +SBAddress::SBAddress (const lldb_private::Address *lldb_object_ptr) : + m_lldb_object_ap () +{ + if (lldb_object_ptr) + m_lldb_object_ap.reset (new lldb_private::Address(*lldb_object_ptr)); +} + +SBAddress::SBAddress (const SBAddress &rhs) : + m_lldb_object_ap () +{ + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::Address(*rhs.m_lldb_object_ap.get())); +} + +SBAddress::~SBAddress () +{ +} + +const SBAddress & +SBAddress::operator = (const SBAddress &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::Address(*rhs.m_lldb_object_ap.get())); + } + return *this; +} + +bool +SBAddress::IsValid () const +{ + return m_lldb_object_ap.get() != NULL && m_lldb_object_ap->IsValid(); +} + +void +SBAddress::SetAddress (const lldb_private::Address *lldb_object_ptr) +{ + if (lldb_object_ptr) + { + if (m_lldb_object_ap.get()) + *m_lldb_object_ap = *lldb_object_ptr; + else + m_lldb_object_ap.reset (new lldb_private::Address(*lldb_object_ptr)); + return; + } + if (m_lldb_object_ap.get()) + m_lldb_object_ap->Clear(); +} + +lldb::addr_t +SBAddress::GetFileAddress () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetFileAddress(); + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +SBAddress::GetLoadAddress (const SBProcess &process) const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetLoadAddress(process.get()); + else + return LLDB_INVALID_ADDRESS; +} + +bool +SBAddress::OffsetAddress (addr_t offset) +{ + if (m_lldb_object_ap.get()) + { + addr_t addr_offset = m_lldb_object_ap->GetOffset(); + if (addr_offset != LLDB_INVALID_ADDRESS) + { + m_lldb_object_ap->SetOffset(addr_offset + offset); + return true; + } + } + return false; +} + + +const lldb_private::Address * +SBAddress::operator->() const +{ + return m_lldb_object_ap.get(); +} + +const lldb_private::Address & +SBAddress::operator*() const +{ + return *m_lldb_object_ap; +} + + diff --git a/lldb/source/API/SBBlock.cpp b/lldb/source/API/SBBlock.cpp new file mode 100644 index 000000000000..536febd99b72 --- /dev/null +++ b/lldb/source/API/SBBlock.cpp @@ -0,0 +1,47 @@ +//===-- 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/Symbol/Block.h" + +using namespace lldb; + + +SBBlock::SBBlock () : + m_lldb_object_ptr (NULL) +{ +} + +SBBlock::SBBlock (lldb_private::Block *lldb_object_ptr) : + m_lldb_object_ptr (lldb_object_ptr) +{ +} + +SBBlock::~SBBlock () +{ + m_lldb_object_ptr = NULL; +} + +bool +SBBlock::IsValid () const +{ + return m_lldb_object_ptr != NULL; +} + +void +SBBlock::AppendVariables (bool can_create, bool get_parent_variables, lldb_private::VariableList *var_list) +{ + if (IsValid()) + { + m_lldb_object_ptr->AppendVariables (can_create, get_parent_variables, var_list); + } +} + + + diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp new file mode 100644 index 000000000000..2cf4e0b824fb --- /dev/null +++ b/lldb/source/API/SBBreakpoint.cpp @@ -0,0 +1,404 @@ +//===-- 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/SBProcess.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/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Target.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_break_sp () +{ +} + +SBBreakpoint::SBBreakpoint (const SBBreakpoint& rhs) : + m_break_sp (rhs.m_break_sp) +{ +} + + +SBBreakpoint::SBBreakpoint (const lldb::BreakpointSP &bp_sp) : + m_break_sp (bp_sp) +{ +} + +SBBreakpoint::~SBBreakpoint() +{ +} + +const SBBreakpoint & +SBBreakpoint::operator = (const SBBreakpoint& rhs) +{ + if (this != &rhs) + { + m_break_sp = rhs.m_break_sp; + } + return *this; +} + +break_id_t +SBBreakpoint::GetID () const +{ + if (m_break_sp) + return m_break_sp->GetID(); + return LLDB_INVALID_BREAK_ID; +} + + +bool +SBBreakpoint::IsValid() const +{ + return m_break_sp; +} + +void +SBBreakpoint::Dump (FILE *f) +{ + if (m_break_sp) + { + if (f == NULL) + f = SBDebugger::GetOutputFileHandle(); + if (f == NULL) + return; + lldb_private::StreamFile str (f); + m_break_sp->Dump (&str); + } +} + +void +SBBreakpoint::ClearAllBreakpointSites () +{ + if (m_break_sp) + m_break_sp->ClearAllBreakpointSites (); +} + +SBBreakpointLocation +SBBreakpoint::FindLocationByAddress (addr_t vm_addr) +{ + SBBreakpointLocation sb_bp_location; + + if (m_break_sp) + { + if (vm_addr != LLDB_INVALID_ADDRESS) + { + Address address; + Process *sb_process = m_break_sp->GetTarget().GetProcessSP().get(); + if (sb_process == NULL || sb_process->ResolveLoadAddress (vm_addr, address) == false) + { + address.SetSection (NULL); + address.SetOffset (vm_addr); + } + sb_bp_location.SetLocation (m_break_sp->FindLocationByAddress (address)); + } + } + return sb_bp_location; +} + +break_id_t +SBBreakpoint::FindLocationIDByAddress (addr_t vm_addr) +{ + break_id_t lldb_id = (break_id_t) 0; + + if (m_break_sp) + { + if (vm_addr != LLDB_INVALID_ADDRESS) + { + Address address; + Process *sb_process = m_break_sp->GetTarget().GetProcessSP().get(); + if (sb_process == NULL || sb_process->ResolveLoadAddress (vm_addr, address) == false) + { + address.SetSection (NULL); + address.SetOffset (vm_addr); + } + lldb_id = m_break_sp->FindLocationIDByAddress (address); + } + } + + return lldb_id; +} + +SBBreakpointLocation +SBBreakpoint::FindLocationByID (break_id_t bp_loc_id) +{ + SBBreakpointLocation sb_bp_location; + + if (m_break_sp) + sb_bp_location.SetLocation (m_break_sp->FindLocationByID (bp_loc_id)); + + return sb_bp_location; +} + +SBBreakpointLocation +SBBreakpoint::GetLocationAtIndex (uint32_t index) +{ + SBBreakpointLocation sb_bp_location; + + if (m_break_sp) + sb_bp_location.SetLocation (m_break_sp->GetLocationAtIndex (index)); + + return sb_bp_location; +} + +void +SBBreakpoint::ListLocations (FILE* f, const char *description_level) +{ + if (f == NULL) + f = SBDebugger::GetOutputFileHandle(); + + if (f == NULL) + return; + + if (m_break_sp) + { + DescriptionLevel level; + if (strcmp (description_level, "brief") == 0) + level = eDescriptionLevelBrief; + else if (strcmp (description_level, "full") == 0) + level = eDescriptionLevelFull; + else if (strcmp (description_level, "verbose") == 0) + level = eDescriptionLevelVerbose; + else + level = eDescriptionLevelBrief; + + StreamFile str (f); + + str.IndentMore(); + int num_locs = m_break_sp->GetNumLocations(); + for (int i = 0; i < num_locs; ++i) + { + BreakpointLocation *loc = m_break_sp->GetLocationAtIndex (i).get(); + loc->GetDescription (&str, level); + str.EOL(); + } + } +} + +void +SBBreakpoint::SetEnabled (bool enable) +{ + if (m_break_sp) + m_break_sp->SetEnabled (enable); +} + +bool +SBBreakpoint::IsEnabled () +{ + if (m_break_sp) + return m_break_sp->IsEnabled(); + else + return false; +} + +void +SBBreakpoint::SetIgnoreCount (int32_t count) +{ + if (m_break_sp) + m_break_sp->SetIgnoreCount (count); +} + +int32_t +SBBreakpoint::GetIgnoreCount () const +{ + if (m_break_sp) + return m_break_sp->GetIgnoreCount(); + else + return 0; +} + +void +SBBreakpoint::SetThreadID (tid_t sb_thread_id) +{ + if (m_break_sp) + m_break_sp->SetThreadID (sb_thread_id); +} + +tid_t +SBBreakpoint::GetThreadID () +{ + tid_t lldb_thread_id = LLDB_INVALID_THREAD_ID; + if (m_break_sp) + lldb_thread_id = m_break_sp->GetThreadID(); + + return lldb_thread_id; +} + +size_t +SBBreakpoint::GetNumResolvedLocations() const +{ + if (m_break_sp) + return m_break_sp->GetNumResolvedLocations(); + else + return 0; +} + +size_t +SBBreakpoint::GetNumLocations() const +{ + if (m_break_sp) + return m_break_sp->GetNumLocations(); + else + return 0; +} + +void +SBBreakpoint::GetDescription (FILE *f, const char *description_level, bool describe_locations) +{ + if (f == NULL) + return; + + if (m_break_sp) + { + DescriptionLevel level; + if (strcmp (description_level, "brief") == 0) + level = eDescriptionLevelBrief; + else if (strcmp (description_level, "full") == 0) + level = eDescriptionLevelFull; + else if (strcmp (description_level, "verbose") == 0) + level = eDescriptionLevelVerbose; + else + level = eDescriptionLevelBrief; + + StreamFile str (f); + + m_break_sp->GetDescription (&str, level); + str.EOL(); + if (describe_locations) + { + //str.IndentMore(); + // int num_locs = m_break_sp->GetNumLocations(); + // for (int i = 0; i < num_locs; ++i) + // { + // BreakpointLocation *loc = m_break_sp->FindLocationByIndex (i); + // loc->GetDescription (&str, level); + // str.EOL(); + // } + ListLocations (f, description_level); + } + } +} + +bool +SBBreakpoint::PrivateBreakpointHitCallback +( + void *baton, + StoppointCallbackContext *ctx, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id +) +{ + BreakpointSP bp_sp(ctx->context.target->GetBreakpointList().FindBreakpointByID(break_id)); + if (baton && bp_sp) + { + CallbackData *data = (CallbackData *)baton; + lldb_private::Breakpoint *bp = bp_sp.get(); + if (bp && data->callback) + { + if (ctx->context.process) + { + SBProcess sb_process (ctx->context.process->GetSP()); + SBThread sb_thread; + SBBreakpointLocation sb_location; + assert (bp_sp); + sb_location.SetLocation (bp_sp->FindLocationByID (break_loc_id)); + if (ctx->context.thread) + sb_thread.SetThread(ctx->context.thread->GetSP()); + + 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) +{ + if (m_break_sp.get()) + { + BatonSP baton_sp(new SBBreakpointCallbackBaton (callback, baton)); + m_break_sp->SetCallback (SBBreakpoint::PrivateBreakpointHitCallback, baton_sp, false); + } +} + + +lldb_private::Breakpoint * +SBBreakpoint::operator->() const +{ + return m_break_sp.get(); +} + +lldb_private::Breakpoint * +SBBreakpoint::get() const +{ + return m_break_sp.get(); +} + +lldb::BreakpointSP & +SBBreakpoint::operator *() +{ + return m_break_sp; +} + +const lldb::BreakpointSP & +SBBreakpoint::operator *() const +{ + return m_break_sp; +} + diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp new file mode 100644 index 000000000000..8bb36bd73240 --- /dev/null +++ b/lldb/source/API/SBBreakpointLocation.cpp @@ -0,0 +1,162 @@ +//===-- 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/SBDebugger.h" + +#include "lldb/lldb-types.h" +#include "lldb/lldb-defines.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" + +using namespace lldb; +using namespace lldb_private; + + + +//class SBBreakpointLocation + +SBBreakpointLocation::SBBreakpointLocation () +{ +} + +SBBreakpointLocation::SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp) : + m_break_loc_sp (break_loc_sp) +{ +} + +SBBreakpointLocation::~SBBreakpointLocation () +{ +} + +bool +SBBreakpointLocation::IsValid() const +{ + return m_break_loc_sp.get() != NULL; +} + +addr_t +SBBreakpointLocation::GetLoadAddress () +{ + addr_t ret_addr = LLDB_INVALID_ADDRESS; + + if (m_break_loc_sp) + { + ret_addr = m_break_loc_sp->GetLoadAddress(); + } + + return ret_addr; +} + +void +SBBreakpointLocation::SetEnabled (bool enabled) +{ + if (m_break_loc_sp) + { + m_break_loc_sp->SetEnabled (enabled); + } +} + +bool +SBBreakpointLocation::IsEnabled () +{ + if (m_break_loc_sp) + return m_break_loc_sp->IsEnabled(); + else + return false; +} + +int32_t +SBBreakpointLocation::GetIgnoreCount () +{ + if (m_break_loc_sp) + return m_break_loc_sp->GetIgnoreCount(); + else + return 0; +} + +void +SBBreakpointLocation::SetIgnoreCount (int32_t n) +{ + if (m_break_loc_sp) + m_break_loc_sp->SetIgnoreCount (n); +} + +void +SBBreakpointLocation::SetThreadID (tid_t thread_id) +{ + if (m_break_loc_sp) + m_break_loc_sp->SetThreadID (thread_id); +} + +tid_t +SBBreakpointLocation::GetThreadID () +{ + tid_t sb_thread_id = (lldb::tid_t) LLDB_INVALID_THREAD_ID; + if (m_break_loc_sp) + sb_thread_id = m_break_loc_sp->GetThreadID(); + + return sb_thread_id; +} + +bool +SBBreakpointLocation::IsResolved () +{ + if (m_break_loc_sp) + return m_break_loc_sp->IsResolved(); + else + return false; +} + +void +SBBreakpointLocation::SetLocation (const lldb::BreakpointLocationSP &break_loc_sp) +{ + if (m_break_loc_sp) + { + // Uninstall the callbacks? + } + m_break_loc_sp = break_loc_sp; +} + +void +SBBreakpointLocation::GetDescription (FILE *f, const char *description_level) +{ + if (f == NULL) + return; + + if (m_break_loc_sp) + { + DescriptionLevel level; + if (strcmp (description_level, "brief") == 0) + level = eDescriptionLevelBrief; + else if (strcmp (description_level, "full") == 0) + level = eDescriptionLevelFull; + else if (strcmp (description_level, "verbose") == 0) + level = eDescriptionLevelVerbose; + else + level = eDescriptionLevelBrief; + + StreamFile str (f); + + m_break_loc_sp->GetDescription (&str, level); + str.EOL(); + } +} + +SBBreakpoint +SBBreakpointLocation::GetBreakpoint () +{ + SBBreakpoint sb_bp; + if (m_break_loc_sp) + *sb_bp = m_break_loc_sp->GetBreakpoint ().GetSP(); + return sb_bp; +} + diff --git a/lldb/source/API/SBBroadcaster.cpp b/lldb/source/API/SBBroadcaster.cpp new file mode 100644 index 000000000000..b9debcfae229 --- /dev/null +++ b/lldb/source/API/SBBroadcaster.cpp @@ -0,0 +1,142 @@ +//===-- 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/lldb-forward-rtti.h" + +#include "SBBroadcaster.h" +#include "SBListener.h" +#include "SBEvent.h" + +using namespace lldb; +using namespace lldb_private; + + +SBBroadcaster::SBBroadcaster () : + m_lldb_object (NULL), + m_lldb_object_owned (false) +{ +} + + +SBBroadcaster::SBBroadcaster (const char *name) : + m_lldb_object (new Broadcaster (name)), + m_lldb_object_owned (true) +{ +} + +SBBroadcaster::SBBroadcaster (lldb_private::Broadcaster *broadcaster, bool owns) : + m_lldb_object (broadcaster), + m_lldb_object_owned (owns) +{ +} + +SBBroadcaster::~SBBroadcaster() +{ + SetLLDBObjectPtr (NULL, false); +} + +void +SBBroadcaster::BroadcastEventByType (uint32_t event_type, bool unique) +{ + if (m_lldb_object == NULL) + return; + + if (unique) + m_lldb_object->BroadcastEventIfUnique (event_type); + else + m_lldb_object->BroadcastEvent (event_type); +} + +void +SBBroadcaster::BroadcastEvent (const SBEvent &event, bool unique) +{ + if (m_lldb_object == NULL) + return; + + EventSP event_sp = event.GetSharedPtr (); + if (unique) + m_lldb_object->BroadcastEventIfUnique (event_sp); + else + m_lldb_object->BroadcastEvent (event_sp); +} + +void +SBBroadcaster::AddInitialEventsToListener (const SBListener &listener, uint32_t requested_events) +{ + if (m_lldb_object) + m_lldb_object->AddInitialEventsToListener (listener.get(), requested_events); +} + +uint32_t +SBBroadcaster::AddListener (const SBListener &listener, uint32_t event_mask) +{ + if (m_lldb_object) + return m_lldb_object->AddListener (listener.get(), event_mask); + return 0; +} + +const char * +SBBroadcaster::GetName () +{ + if (m_lldb_object) + return m_lldb_object->GetBroadcasterName().AsCString(); + return NULL; +} + +bool +SBBroadcaster::EventTypeHasListeners (uint32_t event_type) +{ + if (m_lldb_object) + return m_lldb_object->EventTypeHasListeners (event_type); + return false; +} + +bool +SBBroadcaster::RemoveListener (const SBListener &listener, uint32_t event_mask) +{ + if (m_lldb_object) + return m_lldb_object->RemoveListener (listener.get(), event_mask); + return false; +} + +Broadcaster * +SBBroadcaster::GetLLDBObjectPtr () const +{ + return m_lldb_object; +} + +void +SBBroadcaster::SetLLDBObjectPtr (Broadcaster *broadcaster, bool owns) +{ + if (m_lldb_object && m_lldb_object_owned) + delete m_lldb_object; + m_lldb_object = broadcaster; + m_lldb_object_owned = owns; +} + + +bool +SBBroadcaster::IsValid () const +{ + return m_lldb_object != NULL; +} + +bool +SBBroadcaster::operator == (const SBBroadcaster &rhs) const +{ + return m_lldb_object == rhs.m_lldb_object; + +} + +bool +SBBroadcaster::operator != (const SBBroadcaster &rhs) const +{ + return m_lldb_object != rhs.m_lldb_object; +} diff --git a/lldb/source/API/SBCommandContext.cpp b/lldb/source/API/SBCommandContext.cpp new file mode 100644 index 000000000000..3a1b60aa3148 --- /dev/null +++ b/lldb/source/API/SBCommandContext.cpp @@ -0,0 +1,34 @@ +//===-- SBCommandContext.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/CommandContext.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +#include "SBCommandContext.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBCommandContext::SBCommandContext (CommandContext *lldb_object) : + m_lldb_object (lldb_object) +{ +} + +SBCommandContext::~SBCommandContext () +{ +} + +bool +SBCommandContext::IsValid () const +{ + return m_lldb_object != NULL; +} + diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp new file mode 100644 index 000000000000..1e40320ad600 --- /dev/null +++ b/lldb/source/API/SBCommandInterpreter.cpp @@ -0,0 +1,193 @@ +//===-- 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-types.h" +#include "lldb/Core/Args.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/Listener.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" + +#include "SBBroadcaster.h" +#include "SBDebugger.h" +#include "SBCommandReturnObject.h" +#include "SBCommandContext.h" +#include "SBSourceManager.h" +#include "SBCommandInterpreter.h" +#include "SBProcess.h" +#include "SBTarget.h" +#include "SBListener.h" +#include "SBStringList.h" + +using namespace lldb; +using namespace lldb_private; + + +SBCommandInterpreter::SBCommandInterpreter (CommandInterpreter &interpreter) : + m_interpreter (interpreter) +{ +} + +SBCommandInterpreter::~SBCommandInterpreter () +{ +} + +bool +SBCommandInterpreter::CommandExists (const char *cmd) +{ + return m_interpreter.CommandExists (cmd); +} + +bool +SBCommandInterpreter::AliasExists (const char *cmd) +{ + return m_interpreter.AliasExists (cmd); +} + +bool +SBCommandInterpreter::UserCommandExists (const char *cmd) +{ + return m_interpreter.UserCommandExists (cmd); +} + +lldb::ReturnStatus +SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history) +{ + result.Clear(); + m_interpreter.HandleCommand (command_line, add_to_history, result.GetLLDBObjectRef()); + 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) +{ + int num_completions; + lldb_private::StringList lldb_matches; + num_completions = m_interpreter.HandleCompletion (current_line, cursor, last_char, match_start_point, + max_return_elements, lldb_matches); + + SBStringList temp_list (&lldb_matches); + matches.AppendList (temp_list); + + return num_completions; +} + +const char ** +SBCommandInterpreter::GetEnvironmentVariables () +{ + const Args *env_vars = m_interpreter.GetEnvironmentVariables(); + if (env_vars) + return env_vars->GetConstArgumentVector (); + return NULL; +} + +bool +SBCommandInterpreter::HasCommands () +{ + return m_interpreter.HasCommands(); +} + +bool +SBCommandInterpreter::HasAliases () +{ + return m_interpreter.HasAliases(); +} + +bool +SBCommandInterpreter::HasUserCommands () +{ + return m_interpreter.HasUserCommands (); +} + +bool +SBCommandInterpreter::HasAliasOptions () +{ + return m_interpreter.HasAliasOptions (); +} + +bool +SBCommandInterpreter::HasInterpreterVariables () +{ + return m_interpreter.HasInterpreterVariables (); +} + +SBProcess +SBCommandInterpreter::GetProcess () +{ + SBProcess process; + CommandContext *context = m_interpreter.Context(); + if (context) + { + Target *target = context->GetTarget(); + if (target) + process.SetProcess(target->GetProcessSP()); + } + return process; +} + +ssize_t +SBCommandInterpreter::WriteToScriptInterpreter (const char *src) +{ + if (src) + return WriteToScriptInterpreter (src, strlen(src)); + return 0; +} + +ssize_t +SBCommandInterpreter::WriteToScriptInterpreter (const char *src, size_t src_len) +{ + if (src && src[0]) + { + ScriptInterpreter *script_interpreter = m_interpreter.GetScriptInterpreter(); + if (script_interpreter) + return ::write (script_interpreter->GetMasterFileDescriptor(), src, src_len); + } + return 0; +} + + +CommandInterpreter * +SBCommandInterpreter::GetLLDBObjectPtr () +{ + return &m_interpreter; +} + +CommandInterpreter & +SBCommandInterpreter::GetLLDBObjectRef () +{ + return m_interpreter; +} + +void +SBCommandInterpreter::SourceInitFileInHomeDirectory (SBCommandReturnObject &result) +{ + result.Clear(); + m_interpreter.SourceInitFile (false, result.GetLLDBObjectRef()); +} + +void +SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory (SBCommandReturnObject &result) +{ + result.Clear(); + m_interpreter.SourceInitFile (true, result.GetLLDBObjectRef()); +} + +SBBroadcaster +SBCommandInterpreter::GetBroadcaster () +{ + SBBroadcaster broadcaster (&m_interpreter, false); + return broadcaster; +} + diff --git a/lldb/source/API/SBCommandReturnObject.cpp b/lldb/source/API/SBCommandReturnObject.cpp new file mode 100644 index 000000000000..3910cc4b03d2 --- /dev/null +++ b/lldb/source/API/SBCommandReturnObject.cpp @@ -0,0 +1,148 @@ +//===-- 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/Interpreter/CommandReturnObject.h" + +#include "SBCommandReturnObject.h" + +using namespace lldb; + +SBCommandReturnObject::SBCommandReturnObject () : + m_return_object_ap (new lldb_private::CommandReturnObject ()) +{ +} + +SBCommandReturnObject::~SBCommandReturnObject () +{ + // m_return_object_ap will automatically delete any pointer it owns +} + +bool +SBCommandReturnObject::IsValid() const +{ + return m_return_object_ap.get() != NULL; +} + + +const char * +SBCommandReturnObject::GetOutput () +{ + if (m_return_object_ap.get()) + return m_return_object_ap->GetOutputStream().GetData(); + return NULL; +} + +const char * +SBCommandReturnObject::GetError () +{ + if (m_return_object_ap.get()) + return m_return_object_ap->GetErrorStream().GetData(); + return NULL; +} + +size_t +SBCommandReturnObject::GetOutputSize () +{ + if (m_return_object_ap.get()) + return m_return_object_ap->GetOutputStream().GetSize(); + return 0; +} + +size_t +SBCommandReturnObject::GetErrorSize () +{ + if (m_return_object_ap.get()) + return m_return_object_ap->GetErrorStream().GetSize(); + 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_return_object_ap.get()) + m_return_object_ap->Clear(); +} + +lldb::ReturnStatus +SBCommandReturnObject::GetStatus() +{ + if (m_return_object_ap.get()) + return m_return_object_ap->GetStatus(); + return lldb::eReturnStatusInvalid; +} + +bool +SBCommandReturnObject::Succeeded () +{ + if (m_return_object_ap.get()) + return m_return_object_ap->Succeeded(); + return false; +} + +bool +SBCommandReturnObject::HasResult () +{ + if (m_return_object_ap.get()) + return m_return_object_ap->HasResult(); + return false; +} + +void +SBCommandReturnObject::AppendMessage (const char *message) +{ + if (m_return_object_ap.get()) + m_return_object_ap->AppendMessage (message); +} + +lldb_private::CommandReturnObject * +SBCommandReturnObject::GetLLDBObjectPtr() +{ + return m_return_object_ap.get(); +} + + +lldb_private::CommandReturnObject & +SBCommandReturnObject::GetLLDBObjectRef() +{ + assert(m_return_object_ap.get()); + return *(m_return_object_ap.get()); +} + + +void +SBCommandReturnObject::SetLLDBObjectPtr (lldb_private::CommandReturnObject *ptr) +{ + if (m_return_object_ap.get()) + m_return_object_ap.reset (ptr); +} + diff --git a/lldb/source/API/SBCommunication.cpp b/lldb/source/API/SBCommunication.cpp new file mode 100644 index 000000000000..e2a791750a8d --- /dev/null +++ b/lldb/source/API/SBCommunication.cpp @@ -0,0 +1,194 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + + + +SBCommunication::SBCommunication() : + m_lldb_object (NULL), + m_lldb_object_owned (false) +{ +} + +SBCommunication::SBCommunication(const char * broadcaster_name) : + m_lldb_object (new Communication (broadcaster_name)), + m_lldb_object_owned (true) +{ +} + +SBCommunication::~SBCommunication() +{ + if (m_lldb_object && m_lldb_object_owned) + delete m_lldb_object; + m_lldb_object = NULL; + m_lldb_object_owned = false; +} + +ConnectionStatus +SBCommunication::CheckIfBytesAvailable () +{ + if (m_lldb_object) + return m_lldb_object->BytesAvailable (0, NULL); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +SBCommunication::WaitForBytesAvailableInfinite () +{ + if (m_lldb_object) + return m_lldb_object->BytesAvailable (UINT32_MAX, NULL); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +SBCommunication::WaitForBytesAvailableWithTimeout (uint32_t timeout_usec) +{ + if (m_lldb_object) + return m_lldb_object->BytesAvailable (timeout_usec, NULL); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +SBCommunication::Connect (const char *url) +{ + if (m_lldb_object) + { + if (!m_lldb_object->HasConnection ()) + m_lldb_object->SetConnection (new ConnectionFileDescriptor()); + return m_lldb_object->Connect (url, NULL); + } + return eConnectionStatusNoConnection; +} + +ConnectionStatus +SBCommunication::AdoptFileDesriptor (int fd, bool owns_fd) +{ + if (m_lldb_object) + { + if (m_lldb_object->HasConnection ()) + { + if (m_lldb_object->IsConnected()) + m_lldb_object->Disconnect (); + } + m_lldb_object->SetConnection (new ConnectionFileDescriptor (fd, owns_fd)); + if (m_lldb_object->IsConnected()) + return eConnectionStatusSuccess; + else + return eConnectionStatusLostConnection; + } + return eConnectionStatusNoConnection; +} + + +ConnectionStatus +SBCommunication::Disconnect () +{ + if (m_lldb_object) + return m_lldb_object->Disconnect (); + return eConnectionStatusNoConnection; +} + +bool +SBCommunication::IsConnected () const +{ + if (m_lldb_object) + return m_lldb_object->IsConnected (); + return false; +} + +size_t +SBCommunication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status) +{ + if (m_lldb_object) + return m_lldb_object->Read (dst, dst_len, timeout_usec, status, NULL); + status = eConnectionStatusNoConnection; + return 0; +} + + +size_t +SBCommunication::Write (const void *src, size_t src_len, ConnectionStatus &status) +{ + if (m_lldb_object) + return m_lldb_object->Write (src, src_len, status, NULL); + status = eConnectionStatusNoConnection; + return 0; +} + +bool +SBCommunication::ReadThreadStart () +{ + if (m_lldb_object) + return m_lldb_object->StartReadThread (); + return false; +} + + +bool +SBCommunication::ReadThreadStop () +{ + if (m_lldb_object) + return m_lldb_object->StopReadThread (); + return false; +} + +bool +SBCommunication::ReadThreadIsRunning () +{ + if (m_lldb_object) + return m_lldb_object->ReadThreadIsRunning (); + return false; +} + +bool +SBCommunication::SetReadThreadBytesReceivedCallback +( + ReadThreadBytesReceived callback, + void *callback_baton +) +{ + if (m_lldb_object) + { + m_lldb_object->SetReadThreadBytesReceivedCallback (callback, callback_baton); + return true; + } + return false; +} + +SBBroadcaster +SBCommunication::GetBroadcaster () +{ + SBBroadcaster broadcaster (m_lldb_object, false); + return broadcaster; +} + + +// +//void +//SBCommunication::CreateIfNeeded () +//{ +// if (m_lldb_object == NULL) +// { +// static uint32_t g_broadcaster_num; +// char broadcaster_name[256]; +// ::snprintf (name, broadcaster_name, "%p SBCommunication", this); +// m_lldb_object = new Communication (broadcaster_name); +// m_lldb_object_owned = true; +// } +// assert (m_lldb_object); +//} +// +// diff --git a/lldb/source/API/SBCompileUnit.cpp b/lldb/source/API/SBCompileUnit.cpp new file mode 100644 index 000000000000..a12934a0587b --- /dev/null +++ b/lldb/source/API/SBCompileUnit.cpp @@ -0,0 +1,120 @@ +//===-- 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/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Symbol/LineTable.h" + +using namespace lldb; +using namespace lldb_private; + + +SBCompileUnit::SBCompileUnit () : + m_lldb_object_ptr (NULL) +{ +} + +SBCompileUnit::SBCompileUnit (lldb_private::CompileUnit *lldb_object_ptr) : + m_lldb_object_ptr (lldb_object_ptr) +{ +} + +SBCompileUnit::~SBCompileUnit () +{ + m_lldb_object_ptr = NULL; +} + +SBFileSpec +SBCompileUnit::GetFileSpec () const +{ + SBFileSpec file_spec; + if (m_lldb_object_ptr) + file_spec.SetFileSpec(*m_lldb_object_ptr); + return file_spec; +} + +uint32_t +SBCompileUnit::GetNumLineEntries () const +{ + if (m_lldb_object_ptr) + { + LineTable *line_table = m_lldb_object_ptr->GetLineTable (); + if (line_table) + return line_table->GetSize(); + } + return 0; +} + +SBLineEntry +SBCompileUnit::GetLineEntryAtIndex (uint32_t idx) const +{ + SBLineEntry sb_line_entry; + if (m_lldb_object_ptr) + { + LineTable *line_table = m_lldb_object_ptr->GetLineTable (); + if (line_table) + { + LineEntry line_entry; + if (line_table->GetLineEntryAtIndex(idx, line_entry)) + sb_line_entry.SetLineEntry(line_entry); + } + } + return sb_line_entry; +} + +uint32_t +SBCompileUnit::FindLineEntryIndex (uint32_t start_idx, uint32_t line, SBFileSpec *inline_file_spec) const +{ + if (m_lldb_object_ptr) + { + FileSpec file_spec; + if (inline_file_spec && inline_file_spec->IsValid()) + file_spec = inline_file_spec->ref(); + else + file_spec = *m_lldb_object_ptr; + + return m_lldb_object_ptr->FindLineEntry (start_idx, + line, + inline_file_spec ? inline_file_spec->get() : NULL, + NULL); + } + return UINT32_MAX; +} + +bool +SBCompileUnit::IsValid () const +{ + return m_lldb_object_ptr != NULL; +} + +bool +SBCompileUnit::operator == (const SBCompileUnit &rhs) const +{ + return m_lldb_object_ptr == rhs.m_lldb_object_ptr; +} + +bool +SBCompileUnit::operator != (const SBCompileUnit &rhs) const +{ + return m_lldb_object_ptr != rhs.m_lldb_object_ptr; +} + +const lldb_private::CompileUnit * +SBCompileUnit::operator->() const +{ + return m_lldb_object_ptr; +} + +const lldb_private::CompileUnit & +SBCompileUnit::operator*() const +{ + return *m_lldb_object_ptr; +} diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp new file mode 100644 index 000000000000..d31c93cddb8a --- /dev/null +++ b/lldb/source/API/SBDebugger.cpp @@ -0,0 +1,569 @@ +//===-- 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 "SBDebugger.h" + +#include "lldb/lldb-include.h" +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +#include "SBListener.h" +#include "SBBroadcaster.h" +#include "SBCommandInterpreter.h" +#include "SBCommandReturnObject.h" +#include "SBEvent.h" +#include "SBFrame.h" +#include "SBTarget.h" +#include "SBProcess.h" +#include "SBThread.h" +#include "SBSourceManager.h" +#include "SBInputReader.h" + +using namespace lldb; +using namespace lldb_private; + +void +SBDebugger::Initialize () +{ + Debugger::Initialize(); +} + +void +SBDebugger::Terminate () +{ + Debugger::Terminate(); +} + +void +SBDebugger::SetAsync (bool b) +{ + static bool value_set_once = false; + + if (!value_set_once) + { + value_set_once = true; + Debugger::GetSharedInstance().SetAsyncExecution(b); + } +} + +void +SBDebugger::SetInputFile (const char *tty_name) +{ + // DEPRECATED: will be removed in next submission + FILE *fh = ::fopen (tty_name, "r"); + SetInputFileHandle (fh, true); +} + +void +SBDebugger::SetOutputFile (const char *tty_name) +{ + // DEPRECATED: will be removed in next submission + FILE *fh = ::fopen (tty_name, "w"); + SetOutputFileHandle (fh, true); + SetErrorFileHandle (fh, false); +} + +void +SBDebugger::SetErrorFile (const char *tty_name) +{ + // DEPRECATED: will be removed in next submission +} + + +// 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) +{ + Debugger::GetSharedInstance().SetInputFileHandle (fh, transfer_ownership); +} + +void +SBDebugger::SetOutputFileHandle (FILE *fh, bool transfer_ownership) +{ + Debugger::GetSharedInstance().SetOutputFileHandle (fh, transfer_ownership); +} + +void +SBDebugger::SetErrorFileHandle (FILE *fh, bool transfer_ownership) +{ + Debugger::GetSharedInstance().SetErrorFileHandle (fh, transfer_ownership); +} + +FILE * +SBDebugger::GetInputFileHandle () +{ + return Debugger::GetSharedInstance().GetInputFileHandle(); +} + +FILE * +SBDebugger::GetOutputFileHandle () +{ + return Debugger::GetSharedInstance().GetOutputFileHandle(); +} + +FILE * +SBDebugger::GetErrorFileHandle () +{ + return Debugger::GetSharedInstance().GetErrorFileHandle(); +} + +SBCommandInterpreter +SBDebugger::GetCommandInterpreter () +{ + SBCommandInterpreter sb_interpreter(Debugger::GetSharedInstance().GetCommandInterpreter()); + return sb_interpreter; +} + +void +SBDebugger::HandleCommand (const char *command) +{ + SBProcess process; + SBCommandInterpreter sb_interpreter(Debugger::GetSharedInstance().GetCommandInterpreter()); + SBCommandReturnObject result; + + sb_interpreter.HandleCommand (command, result, false); + + if (GetErrorFileHandle() != NULL) + result.PutError (GetErrorFileHandle()); + if (GetOutputFileHandle() != NULL) + result.PutOutput (GetOutputFileHandle()); + + if (Debugger::GetSharedInstance().GetAsyncExecution() == false) + { + process = GetCommandInterpreter().GetProcess (); + if (process.IsValid()) + { + EventSP event_sp; + Listener &lldb_listener = Debugger::GetSharedInstance().GetListener(); + while (lldb_listener.GetNextEventForBroadcaster (process.get(), event_sp)) + { + SBEvent event(event_sp); + HandleProcessEvent (process, event, GetOutputFileHandle(), GetErrorFileHandle()); + } + } + } +} + +SBListener +SBDebugger::GetListener () +{ + SBListener sb_listener(Debugger::GetSharedInstance().GetListener()); + return sb_listener; +} + +void +SBDebugger::HandleProcessEvent (const SBProcess &process, const SBEvent &event, FILE *out, FILE *err) +{ + const uint32_t event_type = event.GetType(); + char stdio_buffer[1024]; + size_t len; + + if (event_type & Process::eBroadcastBitSTDOUT) + { + while ((len = process.GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0) + if (out != NULL) + ::fwrite (stdio_buffer, 1, len, out); + } + else if (event_type & Process::eBroadcastBitSTDERR) + { + while ((len = process.GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0) + if (out != NULL) + ::fwrite (stdio_buffer, 1, len, out); + } + else if (event_type & Process::eBroadcastBitStateChanged) + { + // Drain any stdout messages. + while ((len = process.GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0) + if (out != NULL) + ::fwrite (stdio_buffer, 1, len, out); + + // Drain any stderr messages. + while ((len = process.GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0) + if (out != NULL) + ::fwrite (stdio_buffer, 1, len, out); + + StateType event_state = SBProcess::GetStateFromEvent (event); + + if (event_state == eStateInvalid) + return; + + bool is_stopped = StateIsStoppedState (event_state); + if (!is_stopped) + process.ReportCurrentState (event, out); + } +} + +void +SBDebugger::UpdateCurrentThread (SBProcess &process) +{ + if (process.IsValid()) + { + SBThread curr_thread = process.GetCurrentThread (); + SBThread thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + if (curr_thread.IsValid()) + { + if (curr_thread.GetStopReason() != eStopReasonInvalid) + curr_thread_stop_reason = curr_thread.GetStopReason (); + } + + if (! curr_thread.IsValid() + || curr_thread_stop_reason == eStopReasonInvalid + || curr_thread_stop_reason == eStopReasonNone) + { + // Prefer a thread that has just completed its plan over another thread as current thread. + SBThread plan_thread; + SBThread other_thread; + const size_t num_threads = process.GetNumThreads (); + size_t i; + for (i = 0; i < num_threads; ++i) + { + thread = process.GetThreadAtIndex(i); + if (thread.GetStopReason () != eStopReasonInvalid) + { + switch (thread.GetStopReason ()) + { + default: + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + if (! other_thread.IsValid()) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (! plan_thread.IsValid()) + plan_thread = thread; + break; + } + } + } + if (plan_thread.IsValid()) + process.SetCurrentThreadByID (plan_thread.GetThreadID()); + else if (other_thread.IsValid()) + process.SetCurrentThreadByID (other_thread.GetThreadID()); + else + { + if (curr_thread.IsValid()) + thread = curr_thread; + else + thread = process.GetThreadAtIndex(0); + + if (thread.IsValid()) + process.SetCurrentThreadByID (thread.GetThreadID()); + } + } + } +} + +void +SBDebugger::ReportCurrentLocation (FILE *out, FILE *err) +{ + if ((out == NULL) || (err == NULL)) + return; + + SBTarget sb_target (GetCurrentTarget()); + if (!sb_target.IsValid()) + { + fprintf (out, "no target\n"); + return; + } + + SBProcess process = sb_target.GetProcess (); + if (process.IsValid()) + { + StateType state = process.GetState(); + + if (StateIsStoppedState (state)) + { + if (state == eStateExited) + { + int exit_status = process.GetExitStatus(); + const char *exit_description = process.GetExitDescription(); + ::fprintf (out, "Process %d exited with status = %i (0x%8.8x) %s\n", + process.GetProcessID(), + exit_status, + exit_status, + exit_description ? exit_description : ""); + } + else + { + fprintf (out, "Process %d %s\n", process.GetProcessID(), StateAsCString (state)); + SBThread current_thread = process.GetThreadAtIndex (0); + if (current_thread.IsValid()) + { + process.DisplayThreadsInfo (out, err, true); + } + else + fprintf (out, "No valid thread found in current process\n"); + } + } + else + fprintf (out, "No current location or status available\n"); + } +} + +SBSourceManager & +SBDebugger::GetSourceManager () +{ + static SourceManager g_lldb_source_manager; + static SBSourceManager g_sb_source_manager (g_lldb_source_manager); + return g_sb_source_manager; +} + + +bool +SBDebugger::GetDefaultArchitecture (char *arch_name, size_t arch_name_len) +{ + if (arch_name && arch_name_len) + { + ArchSpec &default_arch = lldb_private::GetDefaultArchitecture (); + if (default_arch.IsValid()) + { + ::snprintf (arch_name, arch_name_len, "%s", default_arch.AsCString()); + 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()) + { + lldb_private::GetDefaultArchitecture () = arch; + return true; + } + } + return false; +} + +ScriptLanguage +SBDebugger::GetScriptingLanguage (const char *script_language_name) +{ + return Args::StringToScriptLanguage (script_language_name, + eScriptLanguageDefault, + NULL); +} +//pid_t +/* +SBDebugger::AttachByName (const char *process_name, const char *filename) +{ + SBTarget *temp_target = GetCurrentTarget(); + SBTarget sb_target; + pid_t return_pid = (pid_t) LLDB_INVALID_PROCESS_ID; + + if (temp_target == NULL) + { + if (filename != NULL) + { + sb_target = CreateWithFile (filename); + sb_target.SetArch (LLDB_ARCH_DEFAULT); + } + } + else + { + sb_target = *temp_target; + } + + if (sb_target.IsValid()) + { + SBProcess process = sb_target.GetProcess (); + if (process.IsValid()) + { + return_pid = process.AttachByName (process_name); + } + } + return return_pid; +} +*/ + +const char * +SBDebugger::GetVersionString () +{ + return lldb_private::GetVersion(); +} + +const char * +SBDebugger::StateAsCString (lldb::StateType state) +{ + return lldb_private::StateAsCString (state); +} + +bool +SBDebugger::StateIsRunningState (lldb::StateType state) +{ + return lldb_private::StateIsRunningState (state); +} + +bool +SBDebugger::StateIsStoppedState (lldb::StateType state) +{ + return lldb_private::StateIsStoppedState (state); +} + + +SBTarget +SBDebugger::CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple) +{ + ArchSpec arch; + FileSpec file_spec (filename); + arch.SetArchFromTargetTriple(target_triple); + TargetSP target_sp; + Error error (Debugger::GetSharedInstance().GetTargetList().CreateTarget (file_spec, arch, NULL, true, target_sp)); + SBTarget target(target_sp); + return target; +} + +SBTarget +SBDebugger::CreateTargetWithFileAndArch (const char *filename, const char *archname) +{ + FileSpec file (filename); + ArchSpec arch = lldb_private::GetDefaultArchitecture(); + TargetSP target_sp; + Error error; + + if (archname != NULL) + { + ArchSpec arch2 (archname); + error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file, arch2, NULL, true, target_sp); + } + else + { + if (!arch.IsValid()) + arch = LLDB_ARCH_DEFAULT; + + error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file, arch, NULL, true, target_sp); + + if (error.Fail()) + { + if (arch == LLDB_ARCH_DEFAULT_32BIT) + arch = LLDB_ARCH_DEFAULT_64BIT; + else + arch = LLDB_ARCH_DEFAULT_32BIT; + + error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file, arch, NULL, true, target_sp); + } + } + + if (error.Success()) + Debugger::GetSharedInstance().GetTargetList().SetCurrentTarget (target_sp.get()); + else + target_sp.reset(); + + SBTarget sb_target (target_sp); + return sb_target; +} + +SBTarget +SBDebugger::CreateTarget (const char *filename) +{ + FileSpec file (filename); + ArchSpec arch = lldb_private::GetDefaultArchitecture(); + TargetSP target_sp; + Error error; + + if (!arch.IsValid()) + arch = LLDB_ARCH_DEFAULT; + + error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file, arch, NULL, true, target_sp); + + if (error.Fail()) + { + if (arch == LLDB_ARCH_DEFAULT_32BIT) + arch = LLDB_ARCH_DEFAULT_64BIT; + else + arch = LLDB_ARCH_DEFAULT_32BIT; + + error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file, arch, NULL, true, target_sp); + } + + if (!error.Fail()) + Debugger::GetSharedInstance().GetTargetList().SetCurrentTarget (target_sp.get()); + + SBTarget sb_target (target_sp); + return sb_target; +} + +SBTarget +SBDebugger::GetTargetAtIndex (uint32_t idx) +{ + SBTarget sb_target (Debugger::GetSharedInstance().GetTargetList().GetTargetAtIndex (idx)); + return sb_target; +} + +SBTarget +SBDebugger::FindTargetWithProcessID (pid_t pid) +{ + SBTarget sb_target(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcessID (pid)); + return sb_target; +} + +SBTarget +SBDebugger::FindTargetWithFileAndArch (const char *filename, const char *arch_name) +{ + ArchSpec arch; + if (arch_name) + arch.SetArch(arch_name); + return SBTarget (Debugger::GetSharedInstance().GetTargetList().FindTargetWithExecutableAndArchitecture (FileSpec(filename), + arch_name ? &arch : NULL)); +} + +SBTarget +SBDebugger::FindTargetWithLLDBProcess (const lldb::ProcessSP &process_sp) +{ + SBTarget sb_target(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcess (process_sp.get())); + return sb_target; +} + + +uint32_t +SBDebugger::GetNumTargets () +{ + return Debugger::GetSharedInstance().GetTargetList().GetNumTargets ();} + +SBTarget +SBDebugger::GetCurrentTarget () +{ + SBTarget sb_target(Debugger::GetSharedInstance().GetTargetList().GetCurrentTarget ()); + return sb_target; +} + +void +SBDebugger::DispatchInput (void *baton, const void *data, size_t data_len) +{ + Debugger::GetSharedInstance().DispatchInput ((const char *) data, data_len); +} + +void +SBDebugger::PushInputReader (SBInputReader &reader) +{ + if (reader.IsValid()) + { + InputReaderSP reader_sp(*reader); + Debugger::GetSharedInstance().PushInputReader (reader_sp); + } +} diff --git a/lldb/source/API/SBError.cpp b/lldb/source/API/SBError.cpp new file mode 100644 index 000000000000..7c257c94b379 --- /dev/null +++ b/lldb/source/API/SBError.cpp @@ -0,0 +1,179 @@ +//===-- 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/Core/Error.h" + +using namespace lldb; +using namespace lldb_private; + + +SBError::SBError () : + m_lldb_object_ap () +{ +} + +SBError::SBError (const SBError &rhs) : + m_lldb_object_ap () +{ + if (rhs.IsValid()) + m_lldb_object_ap.reset (new Error(*rhs)); +} + + +SBError::~SBError() +{ +} + +const SBError & +SBError::operator = (const SBError &rhs) +{ + if (rhs.IsValid()) + { + if (m_lldb_object_ap.get()) + *m_lldb_object_ap = *rhs; + else + m_lldb_object_ap.reset (new Error(*rhs)); + } + else + { + m_lldb_object_ap.reset(); + } + return *this; +} + + +const char * +SBError::GetCString () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->AsCString(); + return NULL; +} + +void +SBError::Clear () +{ + if (m_lldb_object_ap.get()) + m_lldb_object_ap->Clear(); +} + +bool +SBError::Fail () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->Fail(); + return false; +} + +bool +SBError::Success () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->Success(); + return false; +} + +uint32_t +SBError::GetError () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetError(); + return true; +} + +ErrorType +SBError::GetType () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetType(); + return eErrorTypeInvalid; +} + +void +SBError::SetError (uint32_t err, ErrorType type) +{ + CreateIfNeeded (); + m_lldb_object_ap->SetError (err, type); +} + +void +SBError::SetError (const Error &lldb_error) +{ + CreateIfNeeded (); + *m_lldb_object_ap = lldb_error; +} + + +void +SBError::SetErrorToErrno () +{ + CreateIfNeeded (); + m_lldb_object_ap->SetErrorToErrno (); +} + +void +SBError::SetErrorToGenericError () +{ + CreateIfNeeded (); + m_lldb_object_ap->SetErrorToErrno (); +} + +void +SBError::SetErrorString (const char *err_str) +{ + CreateIfNeeded (); + m_lldb_object_ap->SetErrorString (err_str); +} + +int +SBError::SetErrorStringWithFormat (const char *format, ...) +{ + CreateIfNeeded (); + va_list args; + va_start (args, format); + int num_chars = m_lldb_object_ap->SetErrorStringWithVarArg (format, args); + va_end (args); + return num_chars; +} + +bool +SBError::IsValid () const +{ + return m_lldb_object_ap.get() != NULL; +} + +void +SBError::CreateIfNeeded () +{ + if (m_lldb_object_ap.get() == NULL) + m_lldb_object_ap.reset(new Error ()); +} + + +lldb_private::Error * +SBError::operator->() +{ + return m_lldb_object_ap.get(); +} + +lldb_private::Error * +SBError::get() +{ + return m_lldb_object_ap.get(); +} + + +const lldb_private::Error & +SBError::operator*() const +{ + // Be sure to call "IsValid()" before calling this function or it will crash + return *m_lldb_object_ap; +} + diff --git a/lldb/source/API/SBEvent.cpp b/lldb/source/API/SBEvent.cpp new file mode 100644 index 000000000000..c69957ebefc4 --- /dev/null +++ b/lldb/source/API/SBEvent.cpp @@ -0,0 +1,176 @@ +//===-- 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 "SBEvent.h" +#include "SBBroadcaster.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_lldb_object (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_lldb_object (m_event_sp.get()) +{ +} + +SBEvent::SBEvent (EventSP &event_sp) : + m_event_sp (event_sp), + m_lldb_object (event_sp.get()) +{ +} + +SBEvent::~SBEvent() +{ +} + +void +SBEvent::Dump (FILE *f) const +{ + const Event *lldb_event = GetLLDBObjectPtr(); + if (lldb_event) + { + StreamFile str(f); + lldb_event->Dump ((Stream *) &str); + } +} + +const char * +SBEvent::GetDataFlavor () +{ + Event *lldb_event = SBEvent::GetLLDBObjectPtr(); + if (lldb_event) + return lldb_event->GetData()->GetFlavor().AsCString(); + return NULL; +} + +uint32_t +SBEvent::GetType () const +{ + const Event *lldb_event = SBEvent::GetLLDBObjectPtr(); + if (lldb_event) + return lldb_event->GetType(); + return 0; +} + +SBBroadcaster +SBEvent::GetBroadcaster () const +{ + SBBroadcaster broadcaster; + const Event *lldb_event = SBEvent::GetLLDBObjectPtr(); + if (lldb_event) + broadcaster.SetLLDBObjectPtr (lldb_event->GetBroadcaster(), false); + return broadcaster; +} + +bool +SBEvent::BroadcasterMatchesPtr (const SBBroadcaster *broadcaster) +{ + if (broadcaster) + { + Event *lldb_event = SBEvent::GetLLDBObjectPtr(); + if (lldb_event) + return lldb_event->BroadcasterIs (broadcaster->GetLLDBObjectPtr ()); + } + return false; +} + +bool +SBEvent::BroadcasterMatchesRef (const SBBroadcaster &broadcaster) +{ + Event *lldb_event = SBEvent::GetLLDBObjectPtr(); + if (lldb_event) + return lldb_event->BroadcasterIs (broadcaster.GetLLDBObjectPtr ()); + return false; +} + +void +SBEvent::Clear() +{ + Event *lldb_event = SBEvent::GetLLDBObjectPtr(); + if (lldb_event) + lldb_event->Clear(); +} + +EventSP & +SBEvent::GetSharedPtr () const +{ + return m_event_sp; +} + +Event * +SBEvent::GetLLDBObjectPtr () +{ + // 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_lldb_object... + if (m_event_sp) + m_lldb_object = m_event_sp.get(); + + return m_lldb_object; +} + +const Event * +SBEvent::GetLLDBObjectPtr () 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_lldb_object... + if (m_event_sp) + m_lldb_object = m_event_sp.get(); + + return m_lldb_object; +} + +void +SBEvent::SetEventSP (EventSP &event_sp) +{ + m_event_sp = event_sp; + m_lldb_object = m_event_sp.get(); +} + +void +SBEvent::SetLLDBObjectPtr (Event* event_ptr) +{ + m_lldb_object = event_ptr; + m_event_sp.reset(); +} + +bool +SBEvent::IsValid() const +{ + // Do NOT use m_lldb_object directly!!! Must use the SBEvent::GetLLDBObjectPtr() + // accessor. See comments in SBEvent::GetLLDBObjectPtr().... + return SBEvent::GetLLDBObjectPtr() != NULL; + +} + +const char * +SBEvent::GetCStringFromEvent (const SBEvent &event) +{ + return reinterpret_cast(EventDataBytes::GetBytesFromEvent (event.GetLLDBObjectPtr())); +} + + diff --git a/lldb/source/API/SBFileSpec.cpp b/lldb/source/API/SBFileSpec.cpp new file mode 100644 index 000000000000..48c751177c03 --- /dev/null +++ b/lldb/source/API/SBFileSpec.cpp @@ -0,0 +1,133 @@ +//===-- 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 "lldb/API/SBFileSpec.h" +#include "lldb/Core/FileSpec.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpec::SBFileSpec () : + m_lldb_object_ap() +{ +} + +SBFileSpec::SBFileSpec (const SBFileSpec &rhs) : + m_lldb_object_ap() +{ + if (rhs.m_lldb_object_ap.get()) + m_lldb_object_ap.reset (new FileSpec (*m_lldb_object_ap)); +} + +SBFileSpec::SBFileSpec (const char *path) : + m_lldb_object_ap(new FileSpec (path)) +{ +} + +SBFileSpec::~SBFileSpec () +{ +} + +const SBFileSpec & +SBFileSpec::operator = (const SBFileSpec &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::FileSpec(*rhs.m_lldb_object_ap.get())); + } + return *this; +} + +bool +SBFileSpec::IsValid() const +{ + return m_lldb_object_ap.get() != NULL; +} + +bool +SBFileSpec::Exists () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->Exists(); + return false; +} + + +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 +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetFilename().AsCString(); + return NULL; +} + +const char * +SBFileSpec::GetDirectory() const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetDirectory().AsCString(); + return NULL; +} + +uint32_t +SBFileSpec::GetPath (char *dst_path, size_t dst_len) const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->GetPath (dst_path, dst_len); + + if (dst_path && dst_len) + *dst_path = '\0'; + return 0; +} + + +const lldb_private::FileSpec * +SBFileSpec::operator->() const +{ + return m_lldb_object_ap.get(); +} + +const lldb_private::FileSpec * +SBFileSpec::get() const +{ + return m_lldb_object_ap.get(); +} + + +const lldb_private::FileSpec & +SBFileSpec::operator*() const +{ + return *m_lldb_object_ap.get(); +} + +const lldb_private::FileSpec & +SBFileSpec::ref() const +{ + return *m_lldb_object_ap.get(); +} + + +void +SBFileSpec::SetFileSpec (const lldb_private::FileSpec& fs) +{ + if (m_lldb_object_ap.get()) + *m_lldb_object_ap = fs; + else + m_lldb_object_ap.reset (new FileSpec (fs)); +} + diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp new file mode 100644 index 000000000000..d513e5b726dc --- /dev/null +++ b/lldb/source/API/SBFrame.cpp @@ -0,0 +1,394 @@ +//===-- 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 "SBFrame.h" + +#include +#include + +#include "lldb/lldb-types.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Symbol/Block.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/Thread.h" + +#include "SBDebugger.h" +#include "SBValue.h" +#include "SBAddress.h" +#include "SBSymbolContext.h" +#include "SBThread.h" + +using namespace lldb; +using namespace lldb_private; + +SBFrame::SBFrame () : + m_lldb_object_sp () +{ +} + +SBFrame::SBFrame (const lldb::StackFrameSP &lldb_object_sp) : + m_lldb_object_sp (lldb_object_sp) +{ +} + +SBFrame::~SBFrame() +{ +} + + +void +SBFrame::SetFrame (const lldb::StackFrameSP &lldb_object_sp) +{ + m_lldb_object_sp = lldb_object_sp; +} + + +bool +SBFrame::IsValid() const +{ + return (m_lldb_object_sp.get() != NULL); +} + +SBSymbolContext +SBFrame::GetSymbolContext (uint32_t resolve_scope) const +{ + SBSymbolContext sb_sym_ctx; + if (m_lldb_object_sp) + sb_sym_ctx.SetSymbolContext(&m_lldb_object_sp->GetSymbolContext (resolve_scope)); + return sb_sym_ctx; +} + +SBModule +SBFrame::GetModule () const +{ + SBModule sb_module (m_lldb_object_sp->GetSymbolContext (eSymbolContextModule).module_sp); + return sb_module; +} + +SBCompileUnit +SBFrame::GetCompileUnit () const +{ + SBCompileUnit sb_comp_unit(m_lldb_object_sp->GetSymbolContext (eSymbolContextCompUnit).comp_unit); + return sb_comp_unit; +} + +SBFunction +SBFrame::GetFunction () const +{ + SBFunction sb_function(m_lldb_object_sp->GetSymbolContext (eSymbolContextFunction).function); + return sb_function; +} + +SBBlock +SBFrame::GetBlock () const +{ + SBBlock sb_block(m_lldb_object_sp->GetSymbolContext (eSymbolContextBlock).block); + return sb_block; +} + +SBLineEntry +SBFrame::GetLineEntry () const +{ + SBLineEntry sb_line_entry(&m_lldb_object_sp->GetSymbolContext (eSymbolContextLineEntry).line_entry); + return sb_line_entry; +} + +uint32_t +SBFrame::GetFrameID () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetID(); + else + return UINT32_MAX; +} + + +lldb::addr_t +SBFrame::GetPC () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetPC().GetLoadAddress (&m_lldb_object_sp->GetThread().GetProcess()); + return LLDB_INVALID_ADDRESS; +} + +bool +SBFrame::SetPC (lldb::addr_t new_pc) +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetRegisterContext()->SetPC (new_pc); + return false; +} + +lldb::addr_t +SBFrame::GetSP () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetRegisterContext()->GetSP(); + return LLDB_INVALID_ADDRESS; +} + + +lldb::addr_t +SBFrame::GetFP () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetRegisterContext()->GetFP(); + return LLDB_INVALID_ADDRESS; +} + + +SBAddress +SBFrame::GetPCAddress () const +{ + SBAddress sb_addr; + if (m_lldb_object_sp) + sb_addr.SetAddress (&m_lldb_object_sp->GetPC()); + return sb_addr; +} + +void +SBFrame::Clear() +{ + m_lldb_object_sp.reset(); +} + +SBValue +SBFrame::LookupVar (const char *var_name) +{ + lldb::VariableSP var_sp; + if (IsValid ()) + { + lldb_private::VariableList variable_list; + SBSymbolContext sc = GetSymbolContext (eSymbolContextEverything); + + SBBlock block = sc.GetBlock(); + if (block.IsValid()) + block.AppendVariables (true, true, &variable_list); + + const uint32_t num_variables = variable_list.GetSize(); + + bool found = false; + for (int i = 0; i < num_variables && !found; ++i) + { + var_sp = variable_list.GetVariableAtIndex(i); + if (var_sp + && (var_sp.get()->GetName() == lldb_private::ConstString(var_name))) + found = true; + } + if (!found) + var_sp.reset(); + } + SBValue sb_value (ValueObjectSP (new ValueObjectVariable (var_sp))); + return sb_value; +} + +SBValue +SBFrame::LookupVarInScope (const char *var_name, const char *scope) +{ + lldb::VariableSP var_sp; + if (IsValid()) + { + std::string scope_str = scope; + lldb::ValueType var_scope = eValueTypeInvalid; + // Convert scope_str to be all lowercase; + std::transform (scope_str.begin(), scope_str.end(), scope_str.begin(), ::tolower); + + if (scope_str.compare ("global") == 0) + var_scope = eValueTypeVariableGlobal; + else if (scope_str.compare ("local") == 0) + var_scope = eValueTypeVariableLocal; + else if (scope_str.compare ("parameter") == 0) + var_scope = eValueTypeVariableArgument; + + if (var_scope != eValueTypeInvalid) + { + lldb_private::VariableList variable_list; + SBSymbolContext sc = GetSymbolContext (eSymbolContextEverything); + + SBBlock block = sc.GetBlock(); + if (block.IsValid()) + block.AppendVariables (true, true, &variable_list); + + const uint32_t num_variables = variable_list.GetSize(); + + bool found = false; + for (int i = 0; i < num_variables && !found; ++i) + { + var_sp = variable_list.GetVariableAtIndex(i); + if (var_sp + && (var_sp.get()->GetName() == lldb_private::ConstString(var_name)) + && var_sp.get()->GetScope() == var_scope) + found = true; + } + if (!found) + var_sp.reset(); + } + } + SBValue sb_value (ValueObjectSP (new ValueObjectVariable (var_sp))); + return sb_value; +} + +bool +SBFrame::operator == (const SBFrame &rhs) const +{ + return m_lldb_object_sp.get() == rhs.m_lldb_object_sp.get(); +} + +bool +SBFrame::operator != (const SBFrame &rhs) const +{ + return m_lldb_object_sp.get() != rhs.m_lldb_object_sp.get(); +} + +lldb_private::StackFrame * +SBFrame::operator->() const +{ + return m_lldb_object_sp.get(); +} + +lldb_private::StackFrame * +SBFrame::get() const +{ + return m_lldb_object_sp.get(); +} + + +SBThread +SBFrame::GetThread () const +{ + SBThread sb_thread (m_lldb_object_sp->GetThread().GetSP()); + return sb_thread; +} + +const char * +SBFrame::Disassemble () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->Disassemble(); + return NULL; +} + + + +lldb_private::StackFrame * +SBFrame::GetLLDBObjectPtr () +{ + return m_lldb_object_sp.get(); +} + +SBValueList +SBFrame::GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only) +{ + SBValueList value_list; + if (m_lldb_object_sp) + { + size_t i; + VariableList *variable_list = m_lldb_object_sp->GetVariableList(); + 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; + } + if (add_variable) + { + if (in_scope_only && !variable_sp->IsInScope(m_lldb_object_sp.get())) + continue; + + value_list.Append(ValueObjectSP (new ValueObjectVariable (variable_sp))); + } + } + } + } + } + + if (statics) + { + CompileUnit *frame_comp_unit = m_lldb_object_sp->GetSymbolContext (eSymbolContextCompUnit).comp_unit; + + if (frame_comp_unit) + { + variable_list = frame_comp_unit->GetVariableList(true).get(); + + 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) + { + value_list.Append(ValueObjectSP (new ValueObjectVariable (variable_sp))); + } + } + } + } + } + } + } + return value_list; +} + +lldb::SBValueList +SBFrame::GetRegisters () +{ + SBValueList value_list; + if (m_lldb_object_sp) + { + RegisterContext *reg_ctx = m_lldb_object_sp->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(ValueObjectSP (new ValueObjectRegisterSet (reg_ctx, set_idx))); + } + } + } + return value_list; +} + diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp new file mode 100644 index 000000000000..010a5ec17df8 --- /dev/null +++ b/lldb/source/API/SBFunction.cpp @@ -0,0 +1,64 @@ +//===-- 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/Symbol/Function.h" + +using namespace lldb; + + +SBFunction::SBFunction () : + m_lldb_object_ptr (NULL) +{ +} + +SBFunction::SBFunction (lldb_private::Function *lldb_object_ptr) : + m_lldb_object_ptr (lldb_object_ptr) +{ +} + +SBFunction::~SBFunction () +{ + m_lldb_object_ptr = NULL; +} + +bool +SBFunction::IsValid () const +{ + return m_lldb_object_ptr != NULL; +} + +const char * +SBFunction::GetName() const +{ + if (m_lldb_object_ptr) + return m_lldb_object_ptr->GetMangled().GetName().AsCString(); + return NULL; +} + +const char * +SBFunction::GetMangledName () const +{ + if (m_lldb_object_ptr) + return m_lldb_object_ptr->GetMangled().GetMangledName().AsCString(); + return NULL; +} + +bool +SBFunction::operator == (const SBFunction &rhs) const +{ + return m_lldb_object_ptr == rhs.m_lldb_object_ptr; +} + +bool +SBFunction::operator != (const SBFunction &rhs) const +{ + return m_lldb_object_ptr != rhs.m_lldb_object_ptr; +} diff --git a/lldb/source/API/SBHostOS.cpp b/lldb/source/API/SBHostOS.cpp new file mode 100644 index 000000000000..f0af9cd50695 --- /dev/null +++ b/lldb/source/API/SBHostOS.cpp @@ -0,0 +1,64 @@ +//===-- 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/Core/FileSpec.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; +} + +lldb::thread_t +SBHostOS::ThreadCreate +( + const char *name, + void *(*thread_function)(void *), + void *thread_arg, + SBError *error_ptr +) +{ + 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/lldb/source/API/SBInputReader.cpp b/lldb/source/API/SBInputReader.cpp new file mode 100644 index 000000000000..022d73207ad2 --- /dev/null +++ b/lldb/source/API/SBInputReader.cpp @@ -0,0 +1,169 @@ +//===-- 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/SBInputReader.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStringList.h" +#include "lldb/Core/InputReader.h" + + +using namespace lldb; +using namespace lldb_private; + +SBInputReader::SBInputReader () : + m_reader_sp (), + m_callback_function (NULL), + m_callback_baton (NULL) + +{ +} + +SBInputReader::SBInputReader (const lldb::InputReaderSP &reader_sp) : + m_reader_sp (reader_sp) +{ +} + +SBInputReader::SBInputReader (const SBInputReader &rhs) : + m_reader_sp (rhs.m_reader_sp) +{ +} + +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 +( + Callback callback_function, + void *callback_baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + SBError sb_error; + m_reader_sp.reset (new InputReader ()); + + m_callback_function = callback_function; + m_callback_baton = callback_baton; + + if (m_reader_sp) + { + sb_error.SetError (m_reader_sp->Initialize (SBInputReader::PrivateCallback, + this, + granularity, + end_token, + prompt, + echo)); + } + + if (sb_error.Fail()) + { + m_reader_sp.reset (); + m_callback_function = NULL; + m_callback_baton = NULL; + } + + return sb_error; +} + +bool +SBInputReader::IsValid () const +{ + return (m_reader_sp.get() != NULL); +} + +const SBInputReader & +SBInputReader::operator = (const SBInputReader &rhs) +{ + if (this != &rhs) + m_reader_sp = rhs.m_reader_sp; + return *this; +} + +lldb_private::InputReader * +SBInputReader::operator->() const +{ + return m_reader_sp.get(); +} + +lldb::InputReaderSP & +SBInputReader::operator *() +{ + return m_reader_sp; +} + +const lldb::InputReaderSP & +SBInputReader::operator *() const +{ + return m_reader_sp; +} + +lldb_private::InputReader * +SBInputReader::get() const +{ + return m_reader_sp.get(); +} + +bool +SBInputReader::IsDone () const +{ + if (m_reader_sp) + return m_reader_sp->IsDone(); + else + return true; +} + +void +SBInputReader::SetIsDone (bool value) +{ + if (m_reader_sp) + m_reader_sp->SetIsDone (value); +} + +bool +SBInputReader::IsActive () const +{ + if (m_reader_sp) + return m_reader_sp->IsActive(); + else + return false; +} + +InputReaderGranularity +SBInputReader::GetGranularity () +{ + if (m_reader_sp) + return m_reader_sp->GetGranularity(); + else + return eInputReaderGranularityInvalid; +} diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp new file mode 100644 index 000000000000..564fda0b8cb1 --- /dev/null +++ b/lldb/source/API/SBInstruction.cpp @@ -0,0 +1,74 @@ +//===-- 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/Core/Disassembler.h" + +using namespace lldb; +using namespace lldb_private; + +//SBInstruction::SBInstruction (lldb_private::Disassembler::Instruction *lldb_insn) : +// m_lldb_object_sp (lldb_insn); +//{ +//} + +SBInstruction::SBInstruction () +{ +} + +SBInstruction::~SBInstruction () +{ +} + +//bool +//SBInstruction::IsValid() +//{ +// return (m_lldb_object_sp.get() != NULL); +//} + +//size_t +//SBInstruction::GetByteSize () +//{ +// if (IsValid()) +// { +// return m_lldb_object_sp->GetByteSize(); +// } +// return 0; +//} + +//void +//SBInstruction::SetByteSize (size_T byte_size) +//{ +// if (IsValid ()) +// { +// m_lldb_object_sp->SetByteSize (byte_size); +// } +//} + +//bool +//SBInstruction::DoesBranch () +//{ +// if (IsValid ()) +// { +// return m_lldb_object_sp->DoesBranch (); +// } +// return false; +//} + +void +SBInstruction::Print (FILE *out) +{ + if (out == NULL) + return; + + //StreamFile out_strem (out); + + //m_lldb_object_sp->Dump (out, LLDB_INVALID_ADDRESS, NULL, 0); +} diff --git a/lldb/source/API/SBInstructionList.cpp b/lldb/source/API/SBInstructionList.cpp new file mode 100644 index 000000000000..d34e9d100a95 --- /dev/null +++ b/lldb/source/API/SBInstructionList.cpp @@ -0,0 +1,53 @@ +//===-- 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" + +using namespace lldb; + + +SBInstructionList::SBInstructionList () +{ +} + +SBInstructionList::~SBInstructionList () +{ +} + +size_t +SBInstructionList::GetSize () +{ + return 0; +} + +SBInstruction +SBInstructionList::GetInstructionAtIndex (uint32_t idx) +{ + SBInstruction inst; + return inst; +} + +void +SBInstructionList::Clear () +{ +} + +void +SBInstructionList::AppendInstruction (SBInstruction insn) +{ +} + +void +SBInstructionList::Print (FILE *out) +{ + if (out == NULL) + return; +} + diff --git a/lldb/source/API/SBLineEntry.cpp b/lldb/source/API/SBLineEntry.cpp new file mode 100644 index 000000000000..483540050a32 --- /dev/null +++ b/lldb/source/API/SBLineEntry.cpp @@ -0,0 +1,158 @@ +//===-- 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 "lldb/API/SBLineEntry.h" +#include "lldb/Symbol/LineEntry.h" + +using namespace lldb; + + +SBLineEntry::SBLineEntry () : + m_lldb_object_ap () +{ +} + +SBLineEntry::SBLineEntry (const SBLineEntry &rhs) : + m_lldb_object_ap () +{ + if (rhs.IsValid()) + { + m_lldb_object_ap.reset (new lldb_private::LineEntry (*rhs)); + } +} + + + +SBLineEntry::SBLineEntry (const lldb_private::LineEntry *lldb_object_ptr) : + m_lldb_object_ap () +{ + if (lldb_object_ptr) + m_lldb_object_ap.reset (new lldb_private::LineEntry(*lldb_object_ptr)); +} + +const SBLineEntry & +SBLineEntry::operator = (const SBLineEntry &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::LineEntry(*rhs)); + } + return *this; +} + +void +SBLineEntry::SetLineEntry (const lldb_private::LineEntry &lldb_object_ref) +{ + if (m_lldb_object_ap.get()) + (*m_lldb_object_ap.get()) = lldb_object_ref; + else + m_lldb_object_ap.reset (new lldb_private::LineEntry (lldb_object_ref)); +} + + +SBLineEntry::~SBLineEntry () +{ +} + + +SBAddress +SBLineEntry::GetStartAddress () const +{ + SBAddress sb_address; + if (m_lldb_object_ap.get()) + sb_address.SetAddress(&m_lldb_object_ap->range.GetBaseAddress()); + return sb_address; +} + +SBAddress +SBLineEntry::GetEndAddress () const +{ + SBAddress sb_address; + if (m_lldb_object_ap.get()) + { + sb_address.SetAddress(&m_lldb_object_ap->range.GetBaseAddress()); + sb_address.OffsetAddress(m_lldb_object_ap->range.GetByteSize()); + } + return sb_address; +} + +bool +SBLineEntry::IsValid () const +{ + return m_lldb_object_ap.get() != NULL; +} + + +SBFileSpec +SBLineEntry::GetFileSpec () const +{ + SBFileSpec sb_file_spec; + if (m_lldb_object_ap.get() && m_lldb_object_ap->file) + sb_file_spec.SetFileSpec(m_lldb_object_ap->file); + return sb_file_spec; +} + +uint32_t +SBLineEntry::GetLine () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->line; + return 0; +} + + +uint32_t +SBLineEntry::GetColumn () const +{ + if (m_lldb_object_ap.get()) + return m_lldb_object_ap->column; + return 0; +} + +bool +SBLineEntry::operator == (const SBLineEntry &rhs) const +{ + lldb_private::LineEntry *lhs_ptr = m_lldb_object_ap.get(); + lldb_private::LineEntry *rhs_ptr = rhs.m_lldb_object_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_lldb_object_ap.get(); + lldb_private::LineEntry *rhs_ptr = rhs.m_lldb_object_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_lldb_object_ap.get(); +} + +const lldb_private::LineEntry & +SBLineEntry::operator*() const +{ + return *m_lldb_object_ap; +} + + + + + diff --git a/lldb/source/API/SBListener.cpp b/lldb/source/API/SBListener.cpp new file mode 100644 index 000000000000..6f2cf7bc911b --- /dev/null +++ b/lldb/source/API/SBListener.cpp @@ -0,0 +1,299 @@ +//===-- 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/Core/Listener.h" +#include "lldb/lldb-forward-rtti.h" +#include "lldb/Host/TimeValue.h" + +#include "SBListener.h" +#include "SBEvent.h" +#include "SBBroadcaster.h" + +using namespace lldb; +using namespace lldb_private; + + +SBListener::SBListener () +{ +} + +SBListener::SBListener (const char *name) : + m_lldb_object_ptr (new Listener (name)), + m_lldb_object_ptr_owned (true) +{ +} + +SBListener::SBListener (Listener &listener) : + m_lldb_object_ptr (&listener), + m_lldb_object_ptr_owned (false) +{ +} + +SBListener::~SBListener () +{ + if (m_lldb_object_ptr_owned) + { + if (m_lldb_object_ptr) + { + delete m_lldb_object_ptr; + m_lldb_object_ptr = NULL; + } + } +} + +bool +SBListener::IsValid() const +{ + return m_lldb_object_ptr != NULL; +} + +void +SBListener::AddEvent (const SBEvent &event) +{ + EventSP &event_sp = event.GetSharedPtr (); + if (event_sp) + m_lldb_object_ptr->AddEvent (event_sp); +} + +void +SBListener::Clear () +{ + if (IsValid()) + m_lldb_object_ptr->Clear (); +} + +uint32_t +SBListener::StartListeningForEvents (const SBBroadcaster& broadcaster, uint32_t event_mask) +{ + if (IsValid() && broadcaster.IsValid()) + { + return m_lldb_object_ptr->StartListeningForEvents (broadcaster.GetLLDBObjectPtr (), event_mask); + } + return false; +} + +bool +SBListener::StopListeningForEvents (const SBBroadcaster& broadcaster, uint32_t event_mask) +{ + if (IsValid() && broadcaster.IsValid()) + { + return m_lldb_object_ptr->StopListeningForEvents (broadcaster.GetLLDBObjectPtr (), event_mask); + } + return false; +} + +bool +SBListener::WaitForEvent (uint32_t num_seconds, SBEvent &event) +{ + if (IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + assert (num_seconds != 0); // Take this out after all calls with timeout set to zero have been removed.... + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_lldb_object_ptr->WaitForEvent (time_value.IsValid() ? &time_value : NULL, event_sp)) + { + event.SetEventSP (event_sp); + return true; + } + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::WaitForEventForBroadcaster +( + uint32_t num_seconds, + const SBBroadcaster &broadcaster, + SBEvent &event +) +{ + if (IsValid() && broadcaster.IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_lldb_object_ptr->WaitForEventForBroadcaster (time_value.IsValid() ? &time_value : NULL, + broadcaster.GetLLDBObjectPtr (), + event_sp)) + { + event.SetEventSP (event_sp); + return true; + } + + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::WaitForEventForBroadcasterWithType +( + uint32_t num_seconds, + const SBBroadcaster &broadcaster, + uint32_t event_type_mask, + SBEvent &event +) +{ + if (IsValid() && broadcaster.IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_lldb_object_ptr->WaitForEventForBroadcasterWithType (time_value.IsValid() ? &time_value : NULL, + broadcaster.GetLLDBObjectPtr (), + event_type_mask, + event_sp)) + { + event.SetEventSP (event_sp); + return true; + } + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::PeekAtNextEvent (SBEvent &event) +{ + if (m_lldb_object_ptr) + { + event.SetLLDBObjectPtr (m_lldb_object_ptr->PeekAtNextEvent ()); + return event.IsValid(); + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::PeekAtNextEventForBroadcaster (const SBBroadcaster &broadcaster, SBEvent &event) +{ + if (IsValid() && broadcaster.IsValid()) + { + event.SetLLDBObjectPtr (m_lldb_object_ptr->PeekAtNextEventForBroadcaster (broadcaster.GetLLDBObjectPtr ())); + return event.IsValid(); + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::PeekAtNextEventForBroadcasterWithType (const SBBroadcaster &broadcaster, uint32_t event_type_mask, + SBEvent &event) +{ + if (IsValid() && broadcaster.IsValid()) + { + event.SetLLDBObjectPtr(m_lldb_object_ptr->PeekAtNextEventForBroadcasterWithType (broadcaster.GetLLDBObjectPtr (), event_type_mask)); + return event.IsValid(); + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::GetNextEvent (SBEvent &event) +{ + if (m_lldb_object_ptr) + { + EventSP event_sp; + if (m_lldb_object_ptr->GetNextEvent (event_sp)) + { + event.SetEventSP (event_sp); + return true; + } + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::GetNextEventForBroadcaster (const SBBroadcaster &broadcaster, SBEvent &event) +{ + if (IsValid() && broadcaster.IsValid()) + { + EventSP event_sp; + if (m_lldb_object_ptr->GetNextEventForBroadcaster (broadcaster.GetLLDBObjectPtr (), event_sp)) + { + event.SetEventSP (event_sp); + return true; + } + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::GetNextEventForBroadcasterWithType +( + const SBBroadcaster &broadcaster, + uint32_t event_type_mask, + SBEvent &event +) +{ + if (IsValid() && broadcaster.IsValid()) + { + EventSP event_sp; + if (m_lldb_object_ptr->GetNextEventForBroadcasterWithType (broadcaster.GetLLDBObjectPtr (), + event_type_mask, + event_sp)) + { + event.SetEventSP (event_sp); + return true; + } + } + event.SetLLDBObjectPtr (NULL); + return false; +} + +bool +SBListener::HandleBroadcastEvent (const SBEvent &event) +{ + if (m_lldb_object_ptr) + return m_lldb_object_ptr->HandleBroadcastEvent (event.GetSharedPtr()); + return false; +} + +lldb_private::Listener * +SBListener::operator->() const +{ + return m_lldb_object_ptr; +} + +lldb_private::Listener * +SBListener::get() const +{ + return m_lldb_object_ptr; +} + +lldb_private::Listener & +SBListener::operator *() +{ + return *m_lldb_object_ptr; +} + +const lldb_private::Listener & +SBListener::operator *() const +{ + return *m_lldb_object_ptr; +} + + diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp new file mode 100644 index 000000000000..6a54c2177bf9 --- /dev/null +++ b/lldb/source/API/SBModule.cpp @@ -0,0 +1,107 @@ +//===-- 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/SBFileSpec.h" +#include "lldb/Core/Module.h" + +using namespace lldb; + + +SBModule::SBModule () : + m_lldb_object_sp () +{ +} + +SBModule::SBModule (const lldb::ModuleSP& module_sp) : + m_lldb_object_sp (module_sp) +{ +} + +SBModule::~SBModule () +{ +} + +bool +SBModule::IsValid () const +{ + return m_lldb_object_sp.get() != NULL; +} + +SBFileSpec +SBModule::GetFileSpec () const +{ + SBFileSpec file_spec; + if (m_lldb_object_sp) + file_spec.SetFileSpec(m_lldb_object_sp->GetFileSpec()); + return file_spec; +} + +const uint8_t * +SBModule::GetUUIDBytes () const +{ + if (m_lldb_object_sp) + return (const uint8_t *)m_lldb_object_sp->GetUUID().GetBytes(); + return NULL; +} + + +bool +SBModule::operator == (const SBModule &rhs) const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp.get() == rhs.m_lldb_object_sp.get(); + return false; +} + +bool +SBModule::operator != (const SBModule &rhs) const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp.get() != rhs.m_lldb_object_sp.get(); + return false; +} + +lldb::ModuleSP & +SBModule::operator *() +{ + return m_lldb_object_sp; +} + +lldb_private::Module * +SBModule::operator ->() +{ + return m_lldb_object_sp.get(); +} + +const lldb_private::Module * +SBModule::operator ->() const +{ + return m_lldb_object_sp.get(); +} + +lldb_private::Module * +SBModule::get() +{ + return m_lldb_object_sp.get(); +} + +const lldb_private::Module * +SBModule::get() const +{ + return m_lldb_object_sp.get(); +} + + +void +SBModule::SetModule (const lldb::ModuleSP& module_sp) +{ + m_lldb_object_sp = module_sp; +} + diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp new file mode 100644 index 000000000000..776939c9ea60 --- /dev/null +++ b/lldb/source/API/SBProcess.cpp @@ -0,0 +1,604 @@ +//===-- 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 "SBProcess.h" + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" + +// Project includes + +#include "SBBroadcaster.h" +#include "SBDebugger.h" +#include "SBCommandReturnObject.h" +#include "SBEvent.h" +#include "SBThread.h" +#include "SBStringList.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBProcess::SBProcess () : + m_lldb_object_sp() +{ +} + + +//---------------------------------------------------------------------- +// SBProcess constructor +//---------------------------------------------------------------------- + +SBProcess::SBProcess (const SBProcess& rhs) : + m_lldb_object_sp (rhs.m_lldb_object_sp) +{ +} + + +SBProcess::SBProcess (const lldb::ProcessSP &process_sp) : + m_lldb_object_sp (process_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBProcess::~SBProcess() +{ +} + +void +SBProcess::SetProcess (const ProcessSP &process_sp) +{ + m_lldb_object_sp = process_sp; +} + +void +SBProcess::Clear () +{ + m_lldb_object_sp.reset(); +} + + +bool +SBProcess::IsValid() const +{ + return m_lldb_object_sp.get() != NULL; +} + + +uint32_t +SBProcess::GetNumThreads () +{ + if (m_lldb_object_sp) + { + const bool can_update = true; + return m_lldb_object_sp->GetThreadList().GetSize(can_update); + } + return 0; +} + +SBThread +SBProcess::GetCurrentThread () const +{ + SBThread sb_thread; + if (m_lldb_object_sp) + sb_thread.SetThread (m_lldb_object_sp->GetThreadList().GetCurrentThread()); + return sb_thread; +} + +SBTarget +SBProcess::GetTarget() const +{ + SBTarget sb_target; + if (m_lldb_object_sp) + sb_target = SBDebugger::FindTargetWithLLDBProcess (m_lldb_object_sp); + return sb_target; +} + + +size_t +SBProcess::PutSTDIN (const char *src, size_t src_len) +{ + if (m_lldb_object_sp != NULL) + { + Error error; + return m_lldb_object_sp->PutSTDIN (src, src_len, error); + } + else + return 0; +} + +size_t +SBProcess::GetSTDOUT (char *dst, size_t dst_len) const +{ + if (m_lldb_object_sp != NULL) + { + Error error; + return m_lldb_object_sp->GetSTDOUT (dst, dst_len, error); + } + else + return 0; +} + +size_t +SBProcess::GetSTDERR (char *dst, size_t dst_len) const +{ + if (m_lldb_object_sp != NULL) + { + Error error; + return m_lldb_object_sp->GetSTDERR (dst, dst_len, error); + } + else + return 0; +} + +void +SBProcess::ReportCurrentState (const SBEvent &event, FILE *out) const +{ + if (out == NULL) + return; + + if (m_lldb_object_sp != NULL) + { + const StateType event_state = SBProcess::GetStateFromEvent (event); + char message[1024]; + int message_len = ::snprintf (message, + sizeof (message), + "Process %d %s\n", + m_lldb_object_sp->GetID(), + SBDebugger::StateAsCString (event_state)); + + if (message_len > 0) + ::fwrite (message, 1, message_len, out); + } +} + +void +SBProcess::AppendCurrentStateReport (const SBEvent &event, SBCommandReturnObject &result) +{ + if (m_lldb_object_sp != NULL) + { + const StateType event_state = SBProcess::GetStateFromEvent (event); + char message[1024]; + ::snprintf (message, + sizeof (message), + "Process %d %s\n", + m_lldb_object_sp->GetID(), + SBDebugger::StateAsCString (event_state)); + + result.AppendMessage (message); + } +} + +bool +SBProcess::SetCurrentThread (const SBThread &thread) +{ + if (m_lldb_object_sp != NULL) + return m_lldb_object_sp->GetThreadList().SetCurrentThreadByID (thread.GetThreadID()); + return false; +} + +bool +SBProcess::SetCurrentThreadByID (uint32_t tid) +{ + if (m_lldb_object_sp != NULL) + return m_lldb_object_sp->GetThreadList().SetCurrentThreadByID (tid); + return false; +} + +SBThread +SBProcess::GetThreadAtIndex (size_t index) +{ + SBThread thread; + if (m_lldb_object_sp) + thread.SetThread (m_lldb_object_sp->GetThreadList().GetThreadAtIndex(index)); + return thread; +} + +StateType +SBProcess::GetState () +{ + if (m_lldb_object_sp != NULL) + return m_lldb_object_sp->GetState(); + else + return eStateInvalid; +} + + +int +SBProcess::GetExitStatus () +{ + if (m_lldb_object_sp != NULL) + return m_lldb_object_sp->GetExitStatus (); + else + return 0; +} + +const char * +SBProcess::GetExitDescription () +{ + if (m_lldb_object_sp != NULL) + return m_lldb_object_sp->GetExitDescription (); + else + return NULL; +} + +lldb::pid_t +SBProcess::GetProcessID () +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetID(); + else + return LLDB_INVALID_PROCESS_ID; +} + +uint32_t +SBProcess::GetAddressByteSize () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetAddressByteSize(); + else + return 0; +} + + +void +SBProcess::DisplayThreadsInfo (FILE *out, FILE *err, bool only_threads_with_stop_reason) +{ + if (m_lldb_object_sp != NULL) + { + size_t num_thread_infos_dumped = 0; + size_t num_threads = GetNumThreads(); + + if (out == NULL) + out = SBDebugger::GetOutputFileHandle(); + + if (err == NULL) + err = SBDebugger::GetErrorFileHandle(); + + if ((out == NULL) ||(err == NULL)) + return; + + if (num_threads > 0) + { + Thread::StopInfo thread_stop_info; + SBThread curr_thread (m_lldb_object_sp->GetThreadList().GetCurrentThread()); + for (int i = 0; i < num_threads; ++i) + { + SBThread thread (m_lldb_object_sp->GetThreadList().GetThreadAtIndex(i)); + if (thread.IsValid()) + { + bool is_current_thread = false; + StreamFile str (out); + if (thread == curr_thread) + is_current_thread = true; + StopReason thread_stop_reason = eStopReasonNone; + if (thread->GetStopInfo (&thread_stop_info)) + { + thread_stop_reason = thread_stop_info.GetStopReason(); + if (thread_stop_reason == eStopReasonNone) + { + if (only_threads_with_stop_reason && !is_current_thread) + continue; + } + } + ++num_thread_infos_dumped; + fprintf (out, " %c thread #%u: tid = 0x%4.4x, pc = 0x%16.16llx", + (is_current_thread ? '*' : ' '), + thread->GetIndexID(), thread->GetID(), thread->GetRegisterContext()->GetPC()); + + StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); + if (frame_sp) + { + SymbolContext sc (frame_sp->GetSymbolContext (eSymbolContextEverything)); + fprintf (out, ", where = "); + sc.DumpStopContext (&str, m_lldb_object_sp.get(), frame_sp->GetPC ()); + } + + if (thread_stop_reason != eStopReasonNone) + { + fprintf (out, ", stop reason = "); + thread_stop_info.Dump (&str); + } + + const char *thread_name = thread->GetName(); + if (thread_name && thread_name[0]) + fprintf (out, ", thread_name = '%s'", thread_name); + + fprintf (out, "\n"); + + SBThread sb_thread (thread); + sb_thread.DisplayFramesForCurrentContext (out, err, 0, 1, false, 1); + } + } + } + } +} +bool +SBProcess::WaitUntilProcessHasStopped (SBCommandReturnObject &result) +{ + bool state_changed = false; + + if (IsValid()) + { + EventSP event_sp; + StateType state = m_lldb_object_sp->WaitForStateChangedEvents (NULL, event_sp); + + while (StateIsStoppedState (state)) + { + state = m_lldb_object_sp->WaitForStateChangedEvents (NULL, event_sp); + SBEvent event (event_sp); + AppendCurrentStateReport (event, result); + state_changed = true; + } + } + return state_changed; +} + +SBError +SBProcess::Continue () +{ + SBError sb_error; + if (IsValid()) + sb_error.SetError(m_lldb_object_sp->Resume()); + else + sb_error.SetErrorString ("SBProcess is invalid"); + + return sb_error; +} + + +SBError +SBProcess::Destroy () +{ + SBError sb_error; + if (m_lldb_object_sp) + sb_error.SetError(m_lldb_object_sp->Destroy()); + else + sb_error.SetErrorString ("SBProcess is invalid"); + + return sb_error; +} + + +SBError +SBProcess::Stop () +{ + SBError sb_error; + if (IsValid()) + sb_error.SetError (m_lldb_object_sp->Halt()); + else + sb_error.SetErrorString ("SBProcess is invalid"); + return sb_error; +} + +SBError +SBProcess::Kill () +{ + SBError sb_error; + if (m_lldb_object_sp) + sb_error.SetError (m_lldb_object_sp->Destroy()); + else + sb_error.SetErrorString ("SBProcess is invalid"); + return sb_error; +} + + +SBError +SBProcess::AttachByName (const char *name, bool wait_for_launch) +{ + SBError sb_error; + if (m_lldb_object_sp) + sb_error.SetError (m_lldb_object_sp->Attach (name, wait_for_launch)); + else + sb_error.SetErrorString ("SBProcess is invalid"); + return sb_error; +} + +lldb::pid_t +SBProcess::AttachByPID (lldb::pid_t attach_pid) // DEPRECATED: will be removed in a few builds in favor of SBError AttachByPID(pid_t) +{ + Attach (attach_pid); + return GetProcessID(); +} + + +SBError +SBProcess::Attach (lldb::pid_t attach_pid) +{ + SBError sb_error; + if (m_lldb_object_sp) + sb_error.SetError (m_lldb_object_sp->Attach (attach_pid)); + else + sb_error.SetErrorString ("SBProcess is invalid"); + return sb_error; +} + +SBError +SBProcess::Detach () +{ + SBError sb_error; + if (m_lldb_object_sp) + sb_error.SetError (m_lldb_object_sp->Detach()); + else + sb_error.SetErrorString ("SBProcess is invalid"); + + return sb_error; +} + +SBError +SBProcess::Signal (int signal) +{ + SBError sb_error; + if (m_lldb_object_sp) + sb_error.SetError (m_lldb_object_sp->Signal (signal)); + else + sb_error.SetErrorString ("SBProcess is invalid"); + return sb_error; +} + +void +SBProcess::ListThreads () +{ + FILE *out = SBDebugger::GetOutputFileHandle(); + if (out == NULL) + return; + + if (m_lldb_object_sp) + { + size_t num_threads = GetNumThreads (); + if (num_threads > 0) + { + Thread *cur_thread = m_lldb_object_sp->GetThreadList().GetCurrentThread().get(); + for (int i = 0; i < num_threads; ++i) + { + Thread *thread = m_lldb_object_sp->GetThreadList().GetThreadAtIndex(i).get(); + if (thread) + { + bool is_current_thread = false; + if (thread == cur_thread) + is_current_thread = true; + fprintf (out, " [%u] %c tid = 0x%4.4x, pc = 0x%16.16llx", + i, + (is_current_thread ? '*' : ' '), + thread->GetID(), + thread->GetRegisterContext()->GetPC()); + const char *thread_name = thread->GetName(); + if (thread_name && thread_name[0]) + fprintf (out, ", name = %s", thread_name); + const char *queue_name = thread->GetQueueName(); + if (queue_name && queue_name[0]) + fprintf (out, ", queue = %s", queue_name); + fprintf (out, "\n"); + } + } + } + } +} + +SBThread +SBProcess::GetThreadByID (tid_t sb_thread_id) +{ + SBThread thread; + if (m_lldb_object_sp) + thread.SetThread (m_lldb_object_sp->GetThreadList().FindThreadByID ((tid_t) sb_thread_id)); + return thread; +} + +void +SBProcess::Backtrace (bool all_threads, uint32_t num_frames) +{ + if (m_lldb_object_sp) + { + if (!all_threads) + { + SBDebugger::UpdateCurrentThread (*this); + SBThread cur_thread = GetCurrentThread(); + if (cur_thread.IsValid()) + cur_thread.Backtrace (num_frames); + } + else + { + int num_threads = GetNumThreads (); + for (int i = 0; i < num_threads; ++i) + { + SBThread sb_thread = GetThreadAtIndex (i); + sb_thread.Backtrace (num_frames); + } + } + } +} + +StateType +SBProcess::GetStateFromEvent (const SBEvent &event) +{ + return Process::ProcessEventData::GetStateFromEvent (event.GetLLDBObjectPtr()); +} + + +bool +SBProcess::GetRestartedFromEvent (const SBEvent &event) +{ + return Process::ProcessEventData::GetRestartedFromEvent (event.GetLLDBObjectPtr()); +} + +SBProcess +SBProcess::GetProcessFromEvent (const SBEvent &event) +{ + SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.GetLLDBObjectPtr())); + return process; +} + + +SBBroadcaster +SBProcess::GetBroadcaster () const +{ + SBBroadcaster broadcaster(m_lldb_object_sp.get(), false); + return broadcaster; +} + +lldb_private::Process * +SBProcess::operator->() const +{ + return m_lldb_object_sp.get(); +} + +size_t +SBProcess::ReadMemory (addr_t addr, void *dst, size_t dst_len, SBError &sb_error) +{ + size_t bytes_read = 0; + + if (IsValid()) + { + Error error; + bytes_read = m_lldb_object_sp->ReadMemory (addr, dst, dst_len, error); + sb_error.SetError (error); + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + + return bytes_read; +} + +size_t +SBProcess::WriteMemory (addr_t addr, const void *src, size_t src_len, SBError &sb_error) +{ + size_t bytes_written = 0; + + if (IsValid()) + { + Error error; + bytes_written = m_lldb_object_sp->WriteMemory (addr, src, src_len, error); + sb_error.SetError (error); + } + + return bytes_written; +} + +// Mimic shared pointer... +lldb_private::Process * +SBProcess::get() const +{ + return m_lldb_object_sp.get(); +} + diff --git a/lldb/source/API/SBSourceManager.cpp b/lldb/source/API/SBSourceManager.cpp new file mode 100644 index 000000000000..f2dcf6525ffd --- /dev/null +++ b/lldb/source/API/SBSourceManager.cpp @@ -0,0 +1,65 @@ +//===-- 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 "SBSourceManager.h" + +#include "lldb/API/SBFileSpec.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/SourceManager.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBSourceManager::SBSourceManager (SourceManager& source_manager) : + m_source_manager (source_manager) +{ +} + +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, + FILE *f +) +{ + if (f == NULL) + return 0; + + if (file.IsValid()) + { + StreamFile str (f); + + + return m_source_manager.DisplaySourceLinesWithLineNumbers (*file, + line, + context_before, + context_after, + current_line_cstr, + &str); + } + return 0; +} + +SourceManager & +SBSourceManager::GetLLDBManager () +{ + return m_source_manager; +} diff --git a/lldb/source/API/SBStringList.cpp b/lldb/source/API/SBStringList.cpp new file mode 100644 index 000000000000..b4cfb9b0c75d --- /dev/null +++ b/lldb/source/API/SBStringList.cpp @@ -0,0 +1,134 @@ +//===-- 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_lldb_object_ap () +{ +} + +SBStringList::SBStringList (const lldb_private::StringList *lldb_strings_ptr) : + m_lldb_object_ap () +{ + if (lldb_strings_ptr) + m_lldb_object_ap.reset (new lldb_private::StringList (*lldb_strings_ptr)); +} + +SBStringList::SBStringList (const SBStringList &rhs) : + m_lldb_object_ap () +{ + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::StringList(*rhs)); +} + + + +SBStringList::~SBStringList () +{ +} + + +const SBStringList & +SBStringList::operator = (const SBStringList &rhs) +{ + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::StringList(*rhs)); + + return *this; +} + +const lldb_private::StringList * +SBStringList::operator->() const +{ + return m_lldb_object_ap.get(); +} + +const lldb_private::StringList & +SBStringList::operator*() const +{ + return *m_lldb_object_ap; +} + +bool +SBStringList::IsValid() const +{ + return (m_lldb_object_ap.get() != NULL); +} + +void +SBStringList::AppendString (const char *str) +{ + if (str != NULL) + { + if (IsValid()) + m_lldb_object_ap->AppendString (str); + else + m_lldb_object_ap.reset (new lldb_private::StringList (str)); + } + +} + +void +SBStringList::AppendList (const char **strv, int strc) +{ + if ((strv != NULL) + && (strc > 0)) + { + if (IsValid()) + m_lldb_object_ap->AppendList (strv, strc); + else + m_lldb_object_ap.reset (new lldb_private::StringList (strv, strc)); + } +} + +void +SBStringList::AppendList (SBStringList strings) +{ + if (strings.IsValid()) + { + if (! IsValid()) + m_lldb_object_ap.reset (new lldb_private::StringList()); + m_lldb_object_ap->AppendList (*(strings.m_lldb_object_ap)); + } +} + +uint32_t +SBStringList::GetSize () const +{ + if (IsValid()) + { + return m_lldb_object_ap->GetSize(); + } + return 0; +} + +const char * +SBStringList::GetStringAtIndex (size_t idx) +{ + if (IsValid()) + { + return m_lldb_object_ap->GetStringAtIndex (idx); + } + return NULL; +} + +void +SBStringList::Clear () +{ + if (IsValid()) + { + m_lldb_object_ap->Clear(); + } +} diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp new file mode 100644 index 000000000000..8500c8bd8a6b --- /dev/null +++ b/lldb/source/API/SBSymbol.cpp @@ -0,0 +1,64 @@ +//===-- 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/Symbol/Symbol.h" + +using namespace lldb; + + +SBSymbol::SBSymbol () : + m_lldb_object_ptr (NULL) +{ +} + +SBSymbol::SBSymbol (lldb_private::Symbol *lldb_object_ptr) : + m_lldb_object_ptr (lldb_object_ptr) +{ +} + +SBSymbol::~SBSymbol () +{ + m_lldb_object_ptr = NULL; +} + +bool +SBSymbol::IsValid () const +{ + return m_lldb_object_ptr != NULL; +} + +const char * +SBSymbol::GetName() const +{ + if (m_lldb_object_ptr) + return m_lldb_object_ptr->GetMangled().GetName().AsCString(); + return NULL; +} + +const char * +SBSymbol::GetMangledName () const +{ + if (m_lldb_object_ptr) + return m_lldb_object_ptr->GetMangled().GetMangledName().AsCString(); + return NULL; +} + + +bool +SBSymbol::operator == (const SBSymbol &rhs) const +{ + return m_lldb_object_ptr == rhs.m_lldb_object_ptr; +} + +bool +SBSymbol::operator != (const SBSymbol &rhs) const +{ + return m_lldb_object_ptr != rhs.m_lldb_object_ptr; +} diff --git a/lldb/source/API/SBSymbolContext.cpp b/lldb/source/API/SBSymbolContext.cpp new file mode 100644 index 000000000000..dec852952130 --- /dev/null +++ b/lldb/source/API/SBSymbolContext.cpp @@ -0,0 +1,133 @@ +//===-- 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/Symbol/SymbolContext.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBSymbolContext::SBSymbolContext () : + m_lldb_object_ap () +{ +} + +SBSymbolContext::SBSymbolContext (const SymbolContext *sc_ptr) : + m_lldb_object_ap () +{ + if (sc_ptr) + m_lldb_object_ap.reset (new SymbolContext (*sc_ptr)); +} + +SBSymbolContext::SBSymbolContext (const SBSymbolContext& rhs) : + m_lldb_object_ap () +{ + if (rhs.IsValid()) + *m_lldb_object_ap = *rhs.m_lldb_object_ap; +} + +SBSymbolContext::~SBSymbolContext () +{ +} + +const SBSymbolContext & +SBSymbolContext::operator = (const SBSymbolContext &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::SymbolContext(*rhs.m_lldb_object_ap.get())); + } + return *this; +} + +void +SBSymbolContext::SetSymbolContext (const SymbolContext *sc_ptr) +{ + if (sc_ptr) + { + if (m_lldb_object_ap.get()) + *m_lldb_object_ap = *sc_ptr; + else + m_lldb_object_ap.reset (new SymbolContext (*sc_ptr)); + } + else + { + if (m_lldb_object_ap.get()) + m_lldb_object_ap->Clear(); + } +} + +bool +SBSymbolContext::IsValid () const +{ + return m_lldb_object_ap.get() != NULL; +} + + + +SBModule +SBSymbolContext::GetModule () +{ + SBModule sb_module; + if (m_lldb_object_ap.get()) + sb_module.SetModule(m_lldb_object_ap->module_sp); + return sb_module; +} + +SBCompileUnit +SBSymbolContext::GetCompileUnit () +{ + return SBCompileUnit (m_lldb_object_ap.get() ? m_lldb_object_ap->comp_unit : NULL); +} + +SBFunction +SBSymbolContext::GetFunction () +{ + return SBFunction (m_lldb_object_ap.get() ? m_lldb_object_ap->function : NULL); +} + +SBBlock +SBSymbolContext::GetBlock () +{ + return SBBlock (m_lldb_object_ap.get() ? m_lldb_object_ap->block : NULL); +} + +SBLineEntry +SBSymbolContext::GetLineEntry () +{ + SBLineEntry sb_line_entry; + if (m_lldb_object_ap.get()) + sb_line_entry.SetLineEntry (m_lldb_object_ap->line_entry); + + return sb_line_entry; +} + +SBSymbol +SBSymbolContext::GetSymbol () +{ + return SBSymbol (m_lldb_object_ap.get() ? m_lldb_object_ap->symbol : NULL); +} + +lldb_private::SymbolContext* +SBSymbolContext::operator->() const +{ + return m_lldb_object_ap.get(); +} + +lldb_private::SymbolContext * +SBSymbolContext::GetLLDBObjectPtr() const +{ + return m_lldb_object_ap.get(); +} + + + diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp new file mode 100644 index 000000000000..799be37e8733 --- /dev/null +++ b/lldb/source/API/SBTarget.cpp @@ -0,0 +1,553 @@ +//===-- 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 "SBTarget.h" + +#include "lldb/lldb-include.h" + +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBModule.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/Args.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/STLUtils.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" + +#include "SBDebugger.h" +#include "SBProcess.h" +#include "SBListener.h" +#include "SBBreakpoint.h" + +using namespace lldb; +using namespace lldb_private; + +#define DEFAULT_DISASM_BYTE_SIZE 32 + +//---------------------------------------------------------------------- +// SBTarget constructor +//---------------------------------------------------------------------- +SBTarget::SBTarget () +{ +} + +SBTarget::SBTarget (const SBTarget& rhs) : + m_target_sp (rhs.m_target_sp) +{ +} + +SBTarget::SBTarget(const TargetSP& target_sp) : + m_target_sp (target_sp) +{ +} + +const SBTarget& +SBTarget::Assign (const SBTarget& rhs) +{ + if (this != &rhs) + { + m_target_sp = rhs.m_target_sp; + } + return *this; +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBTarget::~SBTarget() +{ +} + +bool +SBTarget::IsValid () const +{ + return m_target_sp.get() != NULL; +} + +SBProcess +SBTarget::GetProcess () +{ + SBProcess sb_process; + if (IsValid()) + sb_process.SetProcess (m_target_sp->GetProcessSP()); + return sb_process; +} + +SBProcess +SBTarget::CreateProcess () +{ + SBProcess sb_process; + + if (IsValid()) + { + SBListener sb_listener = SBDebugger::GetListener(); + if (sb_listener.IsValid()) + sb_process.SetProcess (m_target_sp->CreateProcess (*sb_listener)); + } + return sb_process; +} + +SBProcess +SBTarget::LaunchProcess +( + char const **argv, + char const **envp, + const char *tty, + bool stop_at_entry +) +{ + SBProcess process(GetProcess ()); + if (!process.IsValid()) + process = CreateProcess(); + if (process.IsValid()) + { + Error error (process->Launch (argv, envp, tty, tty, tty)); + if (error.Success()) + { + if (!stop_at_entry) + { + StateType state = process->WaitForProcessToStop (NULL); + if (state == eStateStopped) + process->Resume(); + } + } + } + return process; +} + +SBFileSpec +SBTarget::GetExecutable () +{ + SBFileSpec exe_file_spec; + if (IsValid()) + { + ModuleSP exe_module_sp (m_target_sp->GetExecutableModule ()); + if (exe_module_sp) + exe_file_spec.SetFileSpec (exe_module_sp->GetFileSpec()); + } + return exe_file_spec; +} + + +bool +SBTarget::DeleteTargetFromList (TargetList *list) +{ + if (IsValid()) + return list->DeleteTarget (m_target_sp); + else + return false; +} + +bool +SBTarget::MakeCurrentTarget () +{ + if (IsValid()) + { + Debugger::GetSharedInstance().GetTargetList().SetCurrentTarget (m_target_sp.get()); + return true; + } + return false; +} + +bool +SBTarget::operator == (const SBTarget &rhs) const +{ + return m_target_sp.get() == rhs.m_target_sp.get(); +} + +bool +SBTarget::operator != (const SBTarget &rhs) const +{ + return m_target_sp.get() != rhs.m_target_sp.get(); +} + +lldb_private::Target * +SBTarget::GetLLDBObjectPtr() +{ + return m_target_sp.get(); +} +const lldb_private::Target * +SBTarget::GetLLDBObjectPtr() const +{ + return m_target_sp.get(); +} + +SBBreakpoint +SBTarget::BreakpointCreateByLocation (const char *file, uint32_t line) +{ + SBBreakpoint sb_bp; + if (file != NULL && line != 0) + sb_bp = BreakpointCreateByLocation (SBFileSpec (file), line); + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByLocation (const SBFileSpec &sb_file_spec, uint32_t line) +{ + SBBreakpoint sb_bp; + if (m_target_sp.get() && line != 0) + *sb_bp = m_target_sp->CreateBreakpoint (NULL, *sb_file_spec, line, true, false); + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByName (const char *symbol_name, const char *module_name) +{ + SBBreakpoint sb_bp; + if (m_target_sp.get() && symbol_name && symbol_name[0]) + { + if (module_name && module_name[0]) + { + FileSpec module_file_spec(module_name); + *sb_bp = m_target_sp->CreateBreakpoint (&module_file_spec, symbol_name, false); + } + else + { + *sb_bp = m_target_sp->CreateBreakpoint (NULL, symbol_name, false); + } + } + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name) +{ + SBBreakpoint sb_bp; + if (m_target_sp.get() && symbol_name_regex && symbol_name_regex[0]) + { + RegularExpression regexp(symbol_name_regex); + + if (module_name && module_name[0]) + { + FileSpec module_file_spec(module_name); + + *sb_bp = m_target_sp->CreateBreakpoint (&module_file_spec, regexp, false); + } + else + { + *sb_bp = m_target_sp->CreateBreakpoint (NULL, regexp, false); + } + } + return sb_bp; +} + + + +SBBreakpoint +SBTarget::BreakpointCreateByAddress (addr_t address) +{ + SBBreakpoint sb_bp; + if (m_target_sp.get()) + *sb_bp = m_target_sp->CreateBreakpoint (address, false); + return sb_bp; +} + +void +SBTarget::ListAllBreakpoints () +{ + FILE *out_file = SBDebugger::GetOutputFileHandle(); + + if (out_file == NULL) + return; + + if (IsValid()) + { + const BreakpointList &bp_list = m_target_sp->GetBreakpointList(); + size_t num_bps = bp_list.GetSize(); + for (int i = 0; i < num_bps; ++i) + { + SBBreakpoint sb_breakpoint (bp_list.GetBreakpointByIndex (i)); + sb_breakpoint.GetDescription (out_file, "full"); + } + } +} + +SBBreakpoint +SBTarget::FindBreakpointByID (break_id_t bp_id) +{ + SBBreakpoint sb_breakpoint; + if (m_target_sp && bp_id != LLDB_INVALID_BREAK_ID) + *sb_breakpoint = m_target_sp->GetBreakpointByID (bp_id); + return sb_breakpoint; +} + + +bool +SBTarget::BreakpointDelete (break_id_t bp_id) +{ + if (m_target_sp) + return m_target_sp->RemoveBreakpointByID (bp_id); + return false; +} + +bool +SBTarget::EnableAllBreakpoints () +{ + if (m_target_sp) + { + m_target_sp->EnableAllBreakpoints (); + return true; + } + return false; +} + +bool +SBTarget::DisableAllBreakpoints () +{ + if (m_target_sp) + { + m_target_sp->DisableAllBreakpoints (); + return true; + } + return false; +} + +bool +SBTarget::DeleteAllBreakpoints () +{ + if (m_target_sp) + { + m_target_sp->RemoveAllBreakpoints (); + return true; + } + return false; +} + + +uint32_t +SBTarget::GetNumModules () const +{ + if (m_target_sp) + return m_target_sp->GetImages().GetSize(); + return 0; +} + +SBModule +SBTarget::FindModule (const SBFileSpec &sb_file_spec) +{ + SBModule sb_module; + if (m_target_sp && sb_file_spec.IsValid()) + sb_module.SetModule (m_target_sp->GetImages().FindFirstModuleForFileSpec (*sb_file_spec, NULL)); + return sb_module; +} + +SBModule +SBTarget::GetModuleAtIndex (uint32_t idx) +{ + SBModule sb_module; + if (m_target_sp) + sb_module.SetModule(m_target_sp->GetImages().GetModuleAtIndex(idx)); + return sb_module; +} + + +SBBroadcaster +SBTarget::GetBroadcaster () const +{ + SBBroadcaster broadcaster(m_target_sp.get(), false); + return broadcaster; +} + +void +SBTarget::Disassemble (lldb::addr_t file_address_start, lldb::addr_t file_address_end, const char *module_name) +{ + if (file_address_start == LLDB_INVALID_ADDRESS) + return; + + FILE *out = SBDebugger::GetOutputFileHandle(); + if (out == NULL) + return; + + if (IsValid()) + { + SBModule module; + if (module_name != NULL) + { + SBFileSpec file_spec (module_name); + module = FindModule (file_spec); + } + ArchSpec arch (m_target_sp->GetArchitecture()); + if (!arch.IsValid()) + return; + Disassembler *disassembler = Disassembler::FindPlugin (arch); + if (disassembler == NULL) + return; + + // For now, we need a process; the disassembly functions insist. If we don't have one already, + // make one. + + SBProcess process = GetProcess(); + if (! process.IsValid()) + process = CreateProcess(); + + ExecutionContext exe_context (process.get()); + + if (file_address_end == LLDB_INVALID_ADDRESS + || file_address_end < file_address_start) + file_address_end = file_address_start + DEFAULT_DISASM_BYTE_SIZE; + + // TO BE FIXED: SOMEHOW WE NEED TO SPECIFY/USE THE MODULE, IF THE USER SPECIFIED ONE. I'M NOT + // SURE HOW TO DO THAT AT THE MOMENT. WE ALSO NEED TO FIGURE OUT WHAT TO DO IF THERE ARE MULTIPLE + // MODULES CONTAINING THE SPECIFIED ADDRESSES (E.G. THEY HAVEN'T ALL LOADED & BEEN GIVEN UNIQUE + // ADDRESSES YET). + + DataExtractor data; + size_t bytes_disassembled = disassembler->ParseInstructions (&exe_context, eAddressTypeLoad, + file_address_start, + file_address_end - file_address_start, data); + + if (bytes_disassembled > 0) + { + size_t num_instructions = disassembler->GetInstructionList().GetSize(); + uint32_t offset = 0; + StreamFile out_stream (out); + + for (size_t i = 0; i < num_instructions; ++i) + { + Disassembler::Instruction *inst = disassembler->GetInstructionList().GetInstructionAtIndex (i); + if (inst) + { + lldb::addr_t cur_addr = file_address_start + offset; + size_t inst_byte_size = inst->GetByteSize(); + inst->Dump (&out_stream, cur_addr, &data, offset, exe_context, false); + out_stream.EOL(); + offset += inst_byte_size; + } + } + } + } +} + +void +SBTarget::Disassemble (const char *function_name, const char *module_name) +{ + if (function_name == NULL) + return; + + FILE *out = SBDebugger::GetOutputFileHandle(); + if (out == NULL) + return; + + if (IsValid()) + { + SBModule module; + + if (module_name != NULL) + { + SBFileSpec file_spec (module_name); + module = FindModule (file_spec); + } + + ArchSpec arch (m_target_sp->GetArchitecture()); + if (!arch.IsValid()) + return; + + Disassembler *disassembler = Disassembler::FindPlugin (arch); + if (disassembler == NULL) + return; + + // For now, we need a process; the disassembly functions insist. If we don't have one already, + // make one. + + SBProcess process = GetProcess(); + if (! process.IsValid() + || process.GetProcessID() == 0) + { + fprintf (out, "Cannot disassemble functions until after process has launched.\n"); + return; + } + + ExecutionContext exe_context (process.get()); + + FileSpec *containing_module = NULL; + + if (module_name != NULL) + containing_module = new FileSpec (module_name); + + SearchFilterSP filter_sp (m_target_sp->GetSearchFilterForModule (containing_module)); + AddressResolverSP resolver_sp (new AddressResolverName (function_name)); + + resolver_sp->ResolveAddress (*filter_sp); + + size_t num_matches_found = resolver_sp->GetNumberOfAddresses(); + + if (num_matches_found == 1) + { + DataExtractor data; + + AddressRange func_addresses = resolver_sp->GetAddressRangeAtIndex (0); + Address start_addr = func_addresses.GetBaseAddress(); + lldb::addr_t num_bytes = func_addresses.GetByteSize(); + + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + size_t bytes_disassembled = 0; + + + if (process.GetProcessID() == 0) + { + // Leave this branch in for now, but it should not be reached, since we exit above if the PID is 0. + addr = start_addr.GetFileAddress (); + bytes_disassembled = disassembler->ParseInstructions (&exe_context, eAddressTypeFile, addr, + num_bytes, data); + + } + else + { + addr = start_addr.GetLoadAddress (process.get()); + bytes_disassembled = disassembler->ParseInstructions (&exe_context, eAddressTypeLoad, addr, + num_bytes, data); + + } + + if (bytes_disassembled > 0) + { + size_t num_instructions = disassembler->GetInstructionList().GetSize(); + uint32_t offset = 0; + StreamFile out_stream (out); + + for (size_t i = 0; i < num_instructions; ++i) + { + Disassembler::Instruction *inst = disassembler->GetInstructionList().GetInstructionAtIndex (i); + if (inst) + { + lldb::addr_t cur_addr = addr + offset; + size_t inst_byte_size = inst->GetByteSize(); + inst->Dump (&out_stream, cur_addr, &data, offset, exe_context, false); + out_stream.EOL(); + offset += inst_byte_size; + } + } + } + } + else if (num_matches_found > 1) + { + // TO BE FIXED: Eventually we want to list/disassemble all functions found. + fprintf (out, "Function '%s' was found in multiple modules; please specify the desired module name.\n", + function_name); + } + else + fprintf (out, "Function '%s' was not found.\n", function_name); + } +} diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp new file mode 100644 index 000000000000..4ae9ba175257 --- /dev/null +++ b/lldb/source/API/SBThread.cpp @@ -0,0 +1,551 @@ +//===-- 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 "SBThread.h" + +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanContinue.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.h" + + +#include "SBAddress.h" +#include "SBFrame.h" +#include "SBSourceManager.h" +#include "SBDebugger.h" +#include "SBProcess.h" + +using namespace lldb; +using namespace lldb_private; + +SBThread::SBThread () : + m_lldb_object_sp () +{ +} + +//---------------------------------------------------------------------- +// Thread constructor +//---------------------------------------------------------------------- +SBThread::SBThread (const ThreadSP& lldb_object_sp) : + m_lldb_object_sp (lldb_object_sp) +{ +} + +SBThread::SBThread (const SBThread &rhs) +{ + m_lldb_object_sp = rhs.m_lldb_object_sp; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBThread::~SBThread() +{ +} + +bool +SBThread::IsValid() const +{ + return m_lldb_object_sp != NULL; +} + +StopReason +SBThread::GetStopReason() +{ + if (m_lldb_object_sp) + { + lldb_private::Thread::StopInfo thread_stop_info; + if (m_lldb_object_sp->GetStopInfo(&thread_stop_info)) + return thread_stop_info.GetStopReason(); + } + return eStopReasonInvalid; +} + +size_t +SBThread::GetStopDescription (char *dst, size_t dst_len) +{ + if (m_lldb_object_sp) + { + lldb_private::Thread::StopInfo thread_stop_info; + if (m_lldb_object_sp->GetStopInfo(&thread_stop_info)) + { + const char *stop_desc = thread_stop_info.GetStopDescription(); + if (stop_desc) + { + if (dst) + return ::snprintf (dst, dst_len, "%s", stop_desc); + else + { + // NULL dst passed in, return the length needed to contain the description + return ::strlen (stop_desc) + 1; // Include the NULL byte for size + } + } + else + { + const char *stop_desc = NULL; + size_t stop_desc_len = 0; + switch (thread_stop_info.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 = m_lldb_object_sp->GetProcess().GetUnixSignals ().GetSignalAsCString (thread_stop_info.GetSignal()); + 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; + } + + if (stop_desc && stop_desc[0]) + { + if (dst) + return ::snprintf (dst, dst_len, "%s", stop_desc) + 1; // Include the NULL byte + + if (stop_desc_len == 0) + stop_desc_len = ::strlen (stop_desc) + 1; // Include the NULL byte + + return stop_desc_len; + } + } + } + } + if (dst) + *dst = 0; + return 0; +} + +void +SBThread::SetThread (const ThreadSP& lldb_object_sp) +{ + m_lldb_object_sp = lldb_object_sp; +} + + +lldb::tid_t +SBThread::GetThreadID () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetID(); + else + return LLDB_INVALID_THREAD_ID; +} + +uint32_t +SBThread::GetIndexID () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetIndexID(); + return LLDB_INVALID_INDEX32; +} +const char * +SBThread::GetName () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetName(); + return NULL; +} + +const char * +SBThread::GetQueueName () const +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetQueueName(); + return NULL; +} + + +void +SBThread::DisplayFramesForCurrentContext (FILE *out, + FILE *err, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source, + uint32_t source_lines_before, + uint32_t source_lines_after) +{ + if ((out == NULL) || (err == NULL)) + return; + + if (m_lldb_object_sp) + { + uint32_t num_stack_frames = m_lldb_object_sp->GetStackFrameCount (); + StackFrameSP frame_sp; + int frame_idx = 0; + + for (frame_idx = first_frame; frame_idx < first_frame + num_frames; ++frame_idx) + { + if (frame_idx >= num_stack_frames) + break; + + frame_sp = m_lldb_object_sp->GetStackFrameAtIndex (frame_idx); + if (!frame_sp) + break; + + SBFrame sb_frame (frame_sp); + if (DisplaySingleFrameForCurrentContext (out, + err, + sb_frame, + show_frame_info, + num_frames_with_source > first_frame - frame_idx, + source_lines_before, + source_lines_after) == false) + break; + } + } +} + +bool +SBThread::DisplaySingleFrameForCurrentContext (FILE *out, + FILE *err, + SBFrame &frame, + bool show_frame_info, + bool show_source, + uint32_t source_lines_after, + uint32_t source_lines_before) +{ + bool success = false; + + if ((out == NULL) || (err == NULL)) + return false; + + if (m_lldb_object_sp && frame.IsValid()) + { + + StreamFile str (out); + + SBSymbolContext sc(frame.GetSymbolContext(eSymbolContextEverything)); + + if (show_frame_info && sc.IsValid()) + { + user_id_t frame_idx = (user_id_t) frame.GetFrameID(); + lldb::addr_t pc = frame.GetPC(); + ::fprintf (out, + " frame #%u: tid = 0x%4.4x, pc = 0x%llx ", + frame_idx, + GetThreadID(), + pc); + sc->DumpStopContext (&str, &m_lldb_object_sp->GetProcess(), *frame.GetPCAddress()); + fprintf (out, "\n"); + success = true; + } + + SBCompileUnit comp_unit(sc.GetCompileUnit()); + if (show_source && comp_unit.IsValid()) + { + success = false; + SBLineEntry line_entry; + if (line_entry.IsValid()) + { + SBSourceManager& source_manager = SBDebugger::GetSourceManager(); + SBFileSpec line_entry_file_spec = line_entry.GetFileSpec(); + + if (line_entry_file_spec.IsValid()) + { + source_manager.DisplaySourceLinesWithLineNumbers (line_entry_file_spec, + line_entry.GetLine(), + source_lines_after, + source_lines_before, "->", + out); + success = true; + } + } + } + } + return success; +} + +void +SBThread::StepOver (lldb::RunMode stop_other_threads) +{ + if (m_lldb_object_sp) + { + bool abort_other_plans = true; + StackFrameSP frame_sp(m_lldb_object_sp->GetStackFrameAtIndex (0)); + + if (frame_sp) + { + if (frame_sp->HasDebugInformation ()) + { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + m_lldb_object_sp->QueueThreadPlanForStepRange (abort_other_plans, + eStepTypeOver, + sc.line_entry.range, + sc, + stop_other_threads); + + } + else + { + m_lldb_object_sp->QueueThreadPlanForStepSingleInstruction (true, + abort_other_plans, + stop_other_threads); + } + } + + Process &process = m_lldb_object_sp->GetProcess(); + // Why do we need to set the current thread by ID here??? + process.GetThreadList().SetCurrentThreadByID (m_lldb_object_sp->GetID()); + process.Resume(); + } +} + +void +SBThread::StepInto (lldb::RunMode stop_other_threads) +{ + if (m_lldb_object_sp) + { + bool abort_other_plans = true; + + StackFrameSP frame_sp(m_lldb_object_sp->GetStackFrameAtIndex (0)); + + if (frame_sp && frame_sp->HasDebugInformation ()) + { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + ThreadPlan *new_plan = m_lldb_object_sp->QueueThreadPlanForStepRange (abort_other_plans, + eStepTypeInto, + sc.line_entry.range, + sc, + stop_other_threads); + if (new_plan) + { + ThreadPlanStepInRange *real_plan = dynamic_cast (new_plan); + if (real_plan) + { + bool avoid_no_debug = true; + if (avoid_no_debug) + real_plan->GetFlags().Set (ThreadPlanShouldStopHere::eAvoidNoDebug); + else + real_plan->GetFlags().Clear (ThreadPlanShouldStopHere::eAvoidNoDebug); + } + } + } + else + { + m_lldb_object_sp->QueueThreadPlanForStepSingleInstruction (false, + abort_other_plans, + stop_other_threads); + } + + Process &process = m_lldb_object_sp->GetProcess(); + // Why do we need to set the current thread by ID here??? + process.GetThreadList().SetCurrentThreadByID (m_lldb_object_sp->GetID()); + process.Resume(); + + } +} + +void +SBThread::StepOut () +{ + if (m_lldb_object_sp) + { + bool abort_other_plans = true; + bool stop_other_threads = true; + + m_lldb_object_sp->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, stop_other_threads, eVoteYes, eVoteNoOpinion); + + Process &process = m_lldb_object_sp->GetProcess(); + process.GetThreadList().SetCurrentThreadByID (m_lldb_object_sp->GetID()); + process.Resume(); + } +} + +void +SBThread::StepInstruction (bool step_over) +{ + if (m_lldb_object_sp) + { + m_lldb_object_sp->QueueThreadPlanForStepSingleInstruction (step_over, true, true); + Process &process = m_lldb_object_sp->GetProcess(); + process.GetThreadList().SetCurrentThreadByID (m_lldb_object_sp->GetID()); + process.Resume(); + } +} + +void +SBThread::RunToAddress (lldb::addr_t addr) +{ + if (m_lldb_object_sp) + { + bool abort_other_plans = true; + bool stop_other_threads = true; + + Address target_addr (NULL, addr); + + m_lldb_object_sp->QueueThreadPlanForRunToAddress (abort_other_plans, target_addr, stop_other_threads); + Process &process = m_lldb_object_sp->GetProcess(); + process.GetThreadList().SetCurrentThreadByID (m_lldb_object_sp->GetID()); + process.Resume(); + } + +} + +void +SBThread::Backtrace (uint32_t num_frames) +{ + bool all_frames = false; + if (num_frames < 1) + all_frames = true; + + FILE *out = SBDebugger::GetOutputFileHandle(); + FILE *err = SBDebugger::GetErrorFileHandle(); + + if ((out == NULL) || (err == NULL)) + return; + + if (m_lldb_object_sp) + { + if (out && err) + { + int max_num_frames = m_lldb_object_sp->GetStackFrameCount(); + int last_frame = max_num_frames; + + if (!all_frames && (num_frames < last_frame)) + last_frame = num_frames; + + StackFrameSP frame_sp; + for (int i = 0; i < last_frame; ++i) + { + frame_sp = m_lldb_object_sp->GetStackFrameAtIndex (i); + if (!frame_sp) + break; + + SBFrame sb_frame (frame_sp); + if (DisplaySingleFrameForCurrentContext ((FILE *) out, (FILE *) err, sb_frame, true, false, 0, 0) == false) + break; + } + } + } +} + +SBProcess +SBThread::GetProcess () +{ + SBProcess process; + if (m_lldb_object_sp) + { + // Have to go up to the target so we can get a shared pointer to our process... + process.SetProcess(m_lldb_object_sp->GetProcess().GetTarget().GetProcessSP()); + } + return process; +} + +uint32_t +SBThread::GetNumFrames () +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetStackFrameCount(); + return 0; +} + +SBFrame +SBThread::GetFrameAtIndex (uint32_t idx) +{ + SBFrame sb_frame; + if (m_lldb_object_sp) + sb_frame.SetFrame (m_lldb_object_sp->GetStackFrameAtIndex (idx)); + return sb_frame; +} + +const lldb::SBThread & +SBThread::operator = (const lldb::SBThread &rhs) +{ + m_lldb_object_sp = rhs.m_lldb_object_sp; + return *this; +} + +bool +SBThread::operator == (const SBThread &rhs) const +{ + return m_lldb_object_sp.get() == rhs.m_lldb_object_sp.get(); +} + +bool +SBThread::operator != (const SBThread &rhs) const +{ + return m_lldb_object_sp.get() != rhs.m_lldb_object_sp.get(); +} + +lldb_private::Thread * +SBThread::GetLLDBObjectPtr () +{ + return m_lldb_object_sp.get(); +} + +const lldb_private::Thread * +SBThread::operator->() const +{ + return m_lldb_object_sp.get(); +} + +const lldb_private::Thread & +SBThread::operator*() const +{ + return *m_lldb_object_sp; +} + +lldb_private::Thread * +SBThread::operator->() +{ + return m_lldb_object_sp.get(); +} + +lldb_private::Thread & +SBThread::operator*() +{ + return *m_lldb_object_sp; +} diff --git a/lldb/source/API/SBType.cpp b/lldb/source/API/SBType.cpp new file mode 100644 index 000000000000..d8e836e3a8c7 --- /dev/null +++ b/lldb/source/API/SBType.cpp @@ -0,0 +1,23 @@ +//===-- 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 "lldb/API/SBType.h" +#include "lldb/Symbol/ClangASTContext.h" + +using namespace lldb; +using namespace lldb_private; + + +bool +SBType::IsPointerType (void *opaque_type) +{ + return ClangASTContext::IsPointerType (opaque_type); +} + + diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp new file mode 100644 index 000000000000..21f9a0eed94e --- /dev/null +++ b/lldb/source/API/SBValue.cpp @@ -0,0 +1,372 @@ +//===-- 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 "SBValue.h" + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "SBProcess.h" +#include "SBTarget.h" +#include "SBThread.h" +#include "SBFrame.h" +#include "SBDebugger.h" + +using namespace lldb; +using namespace lldb_private; + +SBValue::SBValue () : + m_lldb_object_sp () +{ +} + +SBValue::SBValue (const lldb::ValueObjectSP &value_sp) : + m_lldb_object_sp (value_sp) +{ +} + +SBValue::~SBValue() +{ +} + +bool +SBValue::IsValid () const +{ + return (m_lldb_object_sp.get() != NULL); +} + +void +SBValue::Print (FILE *out_file, SBFrame *frame, bool print_type, bool print_value) +{ + if (out_file == NULL) + return; + + if (IsValid()) + { + + SBThread sb_thread = frame->GetThread(); + SBProcess sb_process = sb_thread.GetProcess(); + + lldb_private::StackFrame *lldb_frame = frame->GetLLDBObjectPtr(); + lldb_private::Thread *lldb_thread = sb_thread.GetLLDBObjectPtr(); + lldb_private::Process *lldb_process = sb_process.get(); + + lldb_private::ExecutionContext context (lldb_process, lldb_thread, lldb_frame); + + lldb_private::StreamFile out_stream (out_file); + + out_stream.Printf ("%s ", m_lldb_object_sp->GetName().AsCString (NULL)); + if (! m_lldb_object_sp->IsInScope (lldb_frame)) + out_stream.Printf ("[out-of-scope] "); + if (print_type) + { + out_stream.Printf ("(%s) ", m_lldb_object_sp->GetTypeName().AsCString ("")); + } + + if (print_value) + { + ExecutionContextScope *exe_scope = frame->get(); + const char *val_cstr = m_lldb_object_sp->GetValueAsCString(exe_scope); + const char *err_cstr = m_lldb_object_sp->GetError().AsCString(); + + if (!err_cstr) + { + const char *sum_cstr = m_lldb_object_sp->GetSummaryAsCString(exe_scope); + const bool is_aggregate = + ClangASTContext::IsAggregateType (m_lldb_object_sp->GetOpaqueClangQualType()); + if (val_cstr) + out_stream.Printf ("= %s ", val_cstr); + + if (sum_cstr) + out_stream.Printf ("%s ", sum_cstr); + + if (is_aggregate) + { + out_stream.PutChar ('{'); + const uint32_t num_children = m_lldb_object_sp->GetNumChildren(); + if (num_children) + { + out_stream.IndentMore(); + for (uint32_t idx = 0; idx < num_children; ++idx) + { + lldb::ValueObjectSP child_sp (m_lldb_object_sp->GetChildAtIndex (idx, true)); + if (child_sp.get()) + { + out_stream.EOL(); + out_stream.Indent(); + out_stream.Printf ("%s (%s) = %s", child_sp.get()->GetName().AsCString (""), + child_sp.get()->GetTypeName().AsCString (""), + child_sp.get()->GetValueAsCString(exe_scope)); + } + } + out_stream.IndentLess(); + } + out_stream.EOL(); + out_stream.Indent ("}"); + } + } + } + out_stream.EOL (); + } +} + +const char * +SBValue::GetName() +{ + if (IsValid()) + return m_lldb_object_sp->GetName().AsCString(); + else + return NULL; +} + +const char * +SBValue::GetTypeName () +{ + if (IsValid()) + return m_lldb_object_sp->GetTypeName().AsCString(); + else + return NULL; +} + +size_t +SBValue::GetByteSize () +{ + size_t result = 0; + + if (IsValid()) + result = m_lldb_object_sp->GetByteSize(); + + return result; +} + +bool +SBValue::IsInScope (const SBFrame &frame) +{ + bool result = false; + + if (IsValid()) + result = m_lldb_object_sp->IsInScope (frame.get()); + + return result; +} + +const char * +SBValue::GetValue (const SBFrame &frame) +{ + const char *value_string = NULL; + if ( m_lldb_object_sp) + value_string = m_lldb_object_sp->GetValueAsCString(frame.get()); + return value_string; +} + +bool +SBValue::GetValueDidChange () +{ + if (IsValid()) + return m_lldb_object_sp->GetValueDidChange(); + return false; +} + +const char * +SBValue::GetSummary (const SBFrame &frame) +{ + const char *value_string = NULL; + if ( m_lldb_object_sp) + value_string = m_lldb_object_sp->GetSummaryAsCString(frame.get()); + return value_string; +} + +const char * +SBValue::GetLocation (const SBFrame &frame) +{ + const char *value_string = NULL; + if (IsValid()) + value_string = m_lldb_object_sp->GetLocationAsCString(frame.get()); + return value_string; +} + +bool +SBValue::SetValueFromCString (const SBFrame &frame, const char *value_str) +{ + bool success = false; + if (IsValid()) + success = m_lldb_object_sp->SetValueFromCString (frame.get(), value_str); + return success; +} + +SBValue +SBValue::GetChildAtIndex (uint32_t idx) +{ + lldb::ValueObjectSP child_sp; + + if (IsValid()) + { + child_sp = m_lldb_object_sp->GetChildAtIndex (idx, true); + } + + SBValue sb_value (child_sp); + return sb_value; +} + +uint32_t +SBValue::GetIndexOfChildWithName (const char *name) +{ + if (IsValid()) + return m_lldb_object_sp->GetIndexOfChildWithName (ConstString(name)); + return UINT32_MAX; +} + +SBValue +SBValue::GetChildMemberWithName (const char *name) +{ + lldb::ValueObjectSP child_sp; + const ConstString str_name (name); + + if (IsValid()) + { + child_sp = m_lldb_object_sp->GetChildMemberWithName (str_name, true); + } + + SBValue sb_value (child_sp); + return sb_value; +} + + +uint32_t +SBValue::GetNumChildren () +{ + uint32_t num_children = 0; + + if (IsValid()) + { + num_children = m_lldb_object_sp->GetNumChildren(); + } + + return num_children; +} + +bool +SBValue::ValueIsStale () +{ + bool result = true; + + if (IsValid()) + { + result = m_lldb_object_sp->GetValueIsValid(); + } + + return result; +} + + +SBValue +SBValue::Dereference () +{ + if (IsValid()) + { + if (m_lldb_object_sp->IsPointerType()) + { + return GetChildAtIndex(0); + } + } + return *this; +} + +bool +SBValue::TypeIsPtrType () +{ + bool is_ptr_type = false; + + if (IsValid()) + { + is_ptr_type = m_lldb_object_sp->IsPointerType(); + } + + return is_ptr_type; +} + + +lldb_private::ExecutionContext +SBValue::GetCurrentExecutionContext () +{ + lldb_private::Process *process = NULL; + lldb_private::Thread *thread = NULL; + lldb_private::StackFrame *frame = NULL; + + SBTarget sb_target = SBDebugger::GetCurrentTarget(); + if (sb_target.IsValid()) + { + SBProcess sb_process = sb_target.GetProcess(); + if (sb_process.IsValid()) + { + process = sb_process.get(); + SBThread sb_thread = sb_process.GetCurrentThread(); + if (sb_thread.IsValid()) + { + thread = sb_thread.GetLLDBObjectPtr(); + frame = thread->GetStackFrameAtIndex(0).get(); + lldb_private::ExecutionContext exe_context (process, thread, frame); + return exe_context; + } + else + { + lldb_private::ExecutionContext exe_context (process, NULL, NULL); + return exe_context; + } + } + } + + lldb_private::ExecutionContext exe_context (NULL, NULL, NULL); + return exe_context; +} + + +void * +SBValue::GetOpaqueType() +{ + if (m_lldb_object_sp) + return m_lldb_object_sp->GetOpaqueClangQualType(); + return NULL; +} + +// Mimic shared pointer... +lldb_private::ValueObject * +SBValue::get() const +{ + return m_lldb_object_sp.get(); +} + +lldb_private::ValueObject * +SBValue::operator->() const +{ + return m_lldb_object_sp.get(); +} + +lldb::ValueObjectSP & +SBValue::operator*() +{ + return m_lldb_object_sp; +} + +const lldb::ValueObjectSP & +SBValue::operator*() const +{ + return m_lldb_object_sp; +} diff --git a/lldb/source/API/SBValueList.cpp b/lldb/source/API/SBValueList.cpp new file mode 100644 index 000000000000..e7cbfad53082 --- /dev/null +++ b/lldb/source/API/SBValueList.cpp @@ -0,0 +1,140 @@ +//===-- 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/Core/ValueObjectList.h" + +using namespace lldb; +using namespace lldb_private; + +SBValueList::SBValueList () : + m_lldb_object_ap () +{ +} + +SBValueList::SBValueList (const SBValueList &rhs) : + m_lldb_object_ap () +{ + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::ValueObjectList (*rhs)); +} + +SBValueList::SBValueList (const lldb_private::ValueObjectList *lldb_object_ptr) : + m_lldb_object_ap () +{ + if (lldb_object_ptr) + m_lldb_object_ap.reset (new lldb_private::ValueObjectList (*lldb_object_ptr)); +} + +SBValueList::~SBValueList () +{ +} + +bool +SBValueList::IsValid () const +{ + return (m_lldb_object_ap.get() != NULL); +} + +const SBValueList & +SBValueList::operator = (const SBValueList &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_lldb_object_ap.reset (new lldb_private::ValueObjectList (*rhs)); + else + m_lldb_object_ap.reset (); + } + return *this; +} + +lldb_private::ValueObjectList * +SBValueList::operator->() +{ + return m_lldb_object_ap.get(); +} + +lldb_private::ValueObjectList & +SBValueList::operator*() +{ + return *m_lldb_object_ap; +} + +const lldb_private::ValueObjectList * +SBValueList::operator->() const +{ + return m_lldb_object_ap.get(); +} + +const lldb_private::ValueObjectList & +SBValueList::operator*() const +{ + return *m_lldb_object_ap; +} + +void +SBValueList::Append (const SBValue &val_obj) +{ + if (val_obj.get()) + { + CreateIfNeeded (); + m_lldb_object_ap->Append (*val_obj); + } +} + +void +SBValueList::Append (lldb::ValueObjectSP& val_obj_sp) +{ + if (val_obj_sp) + { + CreateIfNeeded (); + m_lldb_object_ap->Append (val_obj_sp); + } +} + + +SBValue +SBValueList::GetValueAtIndex (uint32_t idx) const +{ + SBValue sb_value; + if (m_lldb_object_ap.get()) + *sb_value = m_lldb_object_ap->GetValueObjectAtIndex (idx); + return sb_value; +} + +uint32_t +SBValueList::GetSize () const +{ + uint32_t size = 0; + if (m_lldb_object_ap.get()) + size = m_lldb_object_ap->GetSize(); + return size; +} + +void +SBValueList::CreateIfNeeded () +{ + if (m_lldb_object_ap.get() == NULL) + m_lldb_object_ap.reset (new ValueObjectList()); +} + + +SBValue +SBValueList::FindValueObjectByUID (lldb::user_id_t uid) +{ + SBValue sb_value; + if ( m_lldb_object_ap.get()) + *sb_value = m_lldb_object_ap->FindValueObjectByUID (uid); + return sb_value; +} + diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp new file mode 100644 index 000000000000..c475cf0f0d79 --- /dev/null +++ b/lldb/source/Breakpoint/Breakpoint.cpp @@ -0,0 +1,516 @@ +//===-- 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/BreakpointResolver.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; +using namespace lldb_private; + +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_target (target), + m_filter_sp (filter_sp), + m_resolver_sp (resolver_sp), + m_options (), + m_locations () +{ +} + +//---------------------------------------------------------------------- +// 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 (Address &addr, bool *new_location) +{ + BreakpointLocationSP bp_loc_sp (m_locations.FindByAddress(addr)); + if (bp_loc_sp) + { + if (new_location) + *new_location = false; + return bp_loc_sp; + } + + bp_loc_sp.reset (new BreakpointLocation (m_locations.GetNextID(), *this, addr)); + m_locations.Add (bp_loc_sp); + bp_loc_sp->ResolveBreakpointSite(); + + if (new_location) + *new_location = true; + return bp_loc_sp; +} + +BreakpointLocationSP +Breakpoint::FindLocationByAddress (Address &addr) +{ + return m_locations.FindByAddress(addr); +} + +break_id_t +Breakpoint::FindLocationIDByAddress (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 (uint32_t index) +{ + return m_locations.GetByIndex(index); +} + +BreakpointLocationSP +Breakpoint::GetLocationSP (BreakpointLocation *bp_loc_ptr) +{ + assert (bp_loc_ptr->GetBreakpoint().GetID() == GetID()); + return m_locations.FindByID(bp_loc_ptr->GetID()); +} + + +// 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 vrs. 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) +{ + m_options.SetEnabled(enable); + if (enable) + m_locations.ResolveAllBreakpointSites(); + else + m_locations.ClearAllBreakpointSites(); +} + +bool +Breakpoint::IsEnabled () +{ + return m_options.IsEnabled(); +} + +void +Breakpoint::SetIgnoreCount (int32_t n) +{ + m_options.SetIgnoreCount(n); +} + +int32_t +Breakpoint::GetIgnoreCount () const +{ + return m_options.GetIgnoreCount(); +} + +void +Breakpoint::SetThreadID (lldb::tid_t thread_id) +{ + m_options.SetThreadID(thread_id); +} + +lldb::tid_t +Breakpoint::GetThreadID () +{ + return m_options.GetThreadID(); +} + +// 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); +} + +// 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) +{ + if (load) + { + // The logic for handling new modules is: + // 1) If the filter rejects this module, then skip it. + // 2) Run through the current location list and if there are any locations + // for that module, we mark the module as "seen" and we don't try to re-resolve + // breakpoint locations for that module. + // However, we do add breakpoint sites to these locations if needed. + // 3) If we don't see this module in our breakpoint location list, call ResolveInModules. + + ModuleList new_modules; // We'll stuff the "unseen" modules in this list, and then resolve + // them after the locations pass. Have to do it this way because + // resolving breakpoints will add new locations potentially. + + for (int i = 0; i < module_list.GetSize(); i++) + { + bool seen = false; + ModuleSP module_sp (module_list.GetModuleAtIndex (i)); + Module *module = module_sp.get(); + if (!m_filter_sp->ModulePasses (module_sp)) + continue; + + for (int i = 0; i < m_locations.GetSize(); i++) + { + BreakpointLocationSP break_loc = m_locations.GetByIndex(i); + const Section *section = break_loc->GetAddress().GetSection(); + if (section == NULL || section->GetModule() == module) + { + 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.AppendInNeeded (module_sp); + + } + if (new_modules.GetSize() > 0) + { + ResolveBreakpointInModules(new_modules); + } + } + else + { + // Go through the currently set locations and if any have breakpoints in + // the module list, then remove their breakpoint sites. + // FIXME: Think about this... Maybe it's better to delete the locations? + // Are we sure that on load-unload-reload the module pointer will remain + // the same? Or do we need to do an equality on modules that is an + // "equivalence"??? + + for (int i = 0; i < module_list.GetSize(); i++) + { + ModuleSP module_sp (module_list.GetModuleAtIndex (i)); + if (!m_filter_sp->ModulePasses (module_sp)) + continue; + + for (int i = 0; i < m_locations.GetSize(); i++) + { + BreakpointLocationSP break_loc = m_locations.GetByIndex(i); + const Section *section = break_loc->GetAddress().GetSection(); + if (section) + { + if (section->GetModule() == module_sp.get()) + break_loc->ClearBreakpointSite(); + } +// else +// { +// Address temp_addr; +// if (module->ResolveLoadAddress(break_loc->GetLoadAddress(), m_target->GetProcess(), temp_addr)) +// break_loc->ClearBreakpointSite(); +// } + } + } + } +} + +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); + StreamString filter_strm; + + + s->Printf("%i ", GetID()); + GetResolverDescription (s); + GetFilterDescription (&filter_strm); + if (filter_strm.GetString().compare ("No Filter") != 0) + { + s->Printf (", "); + GetFilterDescription (s); + } + + const uint32_t num_locations = GetNumLocations (); + const uint32_t num_resolved_locations = GetNumResolvedLocations (); + + switch (level) + { + case lldb::eDescriptionLevelBrief: + case lldb::eDescriptionLevelFull: + if (num_locations > 0) + { + s->Printf(" with %u location%s", num_locations, num_locations > 1 ? "s" : ""); + if (num_resolved_locations > 0) + s->Printf(" (%u resolved)", num_resolved_locations); + s->PutChar(';'); + } + else + { + s->Printf(" with 0 locations (Pending Breakpoint)."); + } + + if (level == lldb::eDescriptionLevelFull) + { + Baton *baton = GetOptions()->GetBaton(); + if (baton) + { + s->EOL (); + s->Indent(); + baton->GetDescription(s, level); + } + } + break; + + case lldb::eDescriptionLevelVerbose: + // Verbose mode does a debug dump of the breakpoint + Dump (s); + Baton *baton = GetOptions()->GetBaton(); + if (baton) + { + s->EOL (); + s->Indent(); + baton->GetDescription(s, level); + } + break; + } + + if (show_locations) + { + s->EOL(); + s->IndentMore(); + for (int i = 0; i < GetNumLocations(); ++i) + { + BreakpointLocation *loc = GetLocationAtIndex(i).get(); + loc->GetDescription(s, level); + s->EOL(); + } + s->IndentLess(); + + } +} + +Breakpoint::BreakpointEventData::BreakpointEventData (Breakpoint::BreakpointEventData::EventSubType sub_type, BreakpointSP &new_breakpoint_sp) : + EventData (), + m_sub_type (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; +} + +Breakpoint::BreakpointEventData::EventSubType +Breakpoint::BreakpointEventData::GetSubType () const +{ + return m_sub_type; +} + +void +Breakpoint::BreakpointEventData::Dump (Stream *s) const +{ +} + +Breakpoint::BreakpointEventData * +Breakpoint::BreakpointEventData::GetEventDataFromEvent (const EventSP &event_sp) +{ + if (event_sp) + { + EventData *event_data = event_sp->GetData(); + if (event_data && event_data->GetFlavor() == BreakpointEventData::GetFlavorString()) + return static_cast (event_sp->GetData()); + } + return NULL; +} + +Breakpoint::BreakpointEventData::EventSubType +Breakpoint::BreakpointEventData::GetSubTypeFromEvent (const EventSP &event_sp) +{ + BreakpointEventData *data = GetEventDataFromEvent (event_sp); + + if (data == NULL) + return eBreakpointInvalidType; + else + return data->GetSubType(); +} + +BreakpointSP +Breakpoint::BreakpointEventData::GetBreakpointFromEvent (const EventSP &event_sp) +{ + BreakpointEventData *data = GetEventDataFromEvent (event_sp); + + if (data == NULL) + { + BreakpointSP ret_val; + return ret_val; + } + else + return data->GetBreakpoint(); +} + + +void +Breakpoint::GetResolverDescription (Stream *s) +{ + if (m_resolver_sp) + m_resolver_sp->GetDescription (s); +} + +void +Breakpoint::GetFilterDescription (Stream *s) +{ + m_filter_sp->GetDescription (s); +} + +const BreakpointSP +Breakpoint::GetSP () +{ + return m_target.GetBreakpointList().FindBreakpointByID (GetID()); +} diff --git a/lldb/source/Breakpoint/BreakpointID.cpp b/lldb/source/Breakpoint/BreakpointID.cpp new file mode 100644 index 000000000000..691e5c07523f --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointID.cpp @@ -0,0 +1,120 @@ +//===-- 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 +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/Breakpoint.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/lldb/source/Breakpoint/BreakpointIDList.cpp b/lldb/source/Breakpoint/BreakpointIDList.cpp new file mode 100644 index 000000000000..895f1b1e3f45 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointIDList.cpp @@ -0,0 +1,348 @@ +//===-- 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/Core/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 () +{ +} + +int +BreakpointIDList::Size() +{ + return m_breakpoint_ids.size(); +} + +BreakpointID & +BreakpointIDList::GetBreakpointIDAtIndex (int index) +{ + if (index < m_breakpoint_ids.size()) + return m_breakpoint_ids[index]; + else + return m_invalid_id; +} + +bool +BreakpointIDList::RemoveBreakpointIDAtIndex (int index) +{ + bool success = false; + if (index < m_breakpoint_ids.size()) + { + BreakpointIDArray::iterator pos; + int i; + + for (pos = m_breakpoint_ids.begin(), i = 0; i != index && pos != m_breakpoint_ids.end(); ++pos, ++i); + assert (i == index); + if (pos != m_breakpoint_ids.end()) + { + m_breakpoint_ids.erase (pos); + success = true; + } + } + return success; +} + +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, int *position) +{ + bool success = false; + BreakpointIDArray::iterator tmp_pos; + + for (int 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()) + { + success = true; + *position = i; + return true; + } + } + + return false; +} + +bool +BreakpointIDList::FindBreakpointID (const char *bp_id_str, int *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, int array_size, CommandReturnObject &result) +{ + if (string_array == NULL) + return; + + for (int 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) +{ + char *range_start; + const char *range_end; + const char *current_arg; + int num_old_args = old_args.GetArgumentCount(); + + for (int i = 0; i < num_old_args; ++i) + { + bool is_range = false; + current_arg = old_args.GetArgumentAtIndex (i); + + int range_start_len = 0; + int range_end_pos = 0; + if (BreakpointIDList::StringContainsIDRangeExpression (current_arg, &range_start_len, &range_end_pos)) + { + is_range = true; + range_start = (char *) malloc (range_start_len + 1); + strncpy (range_start, current_arg, range_start_len); + range_start[range_start_len] = '\0'; + 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 = (char *) current_arg; + range_end = old_args.GetArgumentAtIndex (i+2); + is_range = true; + i = i+2; + } + + 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, &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); + 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; + } + + // 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. + + const BreakpointList& breakpoints = target->GetBreakpointList(); + size_t num_breakpoints = breakpoints.GetSize(); + for (int j = 0; j < num_breakpoints; ++j) + { + Breakpoint *breakpoint = breakpoints.GetBreakpointByIndex (j).get(); + break_id_t cur_bp_id = breakpoint->GetID(); + + if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id)) + continue; + + size_t num_locations = breakpoint->GetNumLocations(); + + if ((cur_bp_id == start_bp_id) && (start_loc_id != LLDB_INVALID_BREAK_ID)) + { + for (int k = 0; k < num_locations; ++k) + { + BreakpointLocation * bp_loc = breakpoint->GetLocationAtIndex(k).get(); + if (bp_loc->GetID() >= start_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 (int 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, const char **range_start, +// const **range_end) +bool +BreakpointIDList::StringContainsIDRangeExpression (const char *in_string, int *range_start_len, int *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 = NULL; + //*range_end = NULL; + *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]; + int 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/lldb/source/Breakpoint/BreakpointList.cpp b/lldb/source/Breakpoint/BreakpointList.cpp new file mode 100644 index 000000000000..c10aa770b827 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointList.cpp @@ -0,0 +1,198 @@ +//===-- 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 + +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) +{ + Mutex::Locker locker(m_mutex); + // Internal breakpoint IDs are negative, normal ones are positive + bp->SetID (m_is_internal ? --m_next_break_id : ++m_next_break_id); + m_breakpoints.push_back(bp); + return bp->GetID(); +} + +bool +BreakpointList::Remove (break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); // Predicate + if (pos != m_breakpoints.end()) + { + m_breakpoints.erase(pos); + 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 () +{ + Mutex::Locker locker(m_mutex); + ClearAllBreakpointSites (); + + 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: ", (int)sizeof(void*) * 2, 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::GetBreakpointByIndex (uint32_t i) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + uint32_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::GetBreakpointByIndex (uint32_t i) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator end = m_breakpoints.end(); + bp_collection::const_iterator pos; + uint32_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::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 (); + +} diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp new file mode 100644 index 000000000000..5c0e81df3811 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -0,0 +1,389 @@ +//===-- BreakpointLocation.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 +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/StreamString.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocation::BreakpointLocation +( + break_id_t loc_id, + Breakpoint &owner, + Address &addr, + lldb::tid_t tid, + bool hardware +) : + StoppointLocation (loc_id, addr.GetLoadAddress(owner.GetTarget().GetProcessSP().get()), tid, hardware), + m_address (addr), + m_owner (owner), + m_options_ap (), + m_bp_site_sp () +{ +} + +BreakpointLocation::~BreakpointLocation() +{ + ClearBreakpointSite(); +} + +lldb::addr_t +BreakpointLocation::GetLoadAddress () +{ + return m_address.GetLoadAddress(m_owner.GetTarget().GetProcessSP().get()); +} + +Address & +BreakpointLocation::GetAddress () +{ + return m_address; +} + +Breakpoint & +BreakpointLocation::GetBreakpoint () +{ + return m_owner; +} + +bool +BreakpointLocation::IsEnabled () +{ + 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(); + } +} + +void +BreakpointLocation::SetThreadID (lldb::tid_t thread_id) +{ + GetLocationOptions()->SetThreadID(thread_id); +} + +lldb::tid_t +BreakpointLocation::GetThreadID () +{ + return GetOptionsNoCopy()->GetThreadID(); +} + +bool +BreakpointLocation::InvokeCallback (StoppointCallbackContext *context) +{ + bool owner_result; + + owner_result = m_owner.InvokeCallback (context, GetID()); + if (owner_result == false) + return false; + else if (m_options_ap.get() != NULL) + return m_options_ap->InvokeCallback (context, m_owner.GetID(), GetID()); + else + return true; +} + +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); +} + +void +BreakpointLocation::SetCallback (BreakpointHitCallback callback, const BatonSP &baton_sp, + bool is_synchronous) +{ + GetLocationOptions()->SetCallback (callback, baton_sp, is_synchronous); +} + +void +BreakpointLocation::ClearCallback () +{ + GetLocationOptions()->ClearCallback(); +} + +int32_t +BreakpointLocation::GetIgnoreCount () +{ + return GetOptionsNoCopy()->GetIgnoreCount(); +} + +void +BreakpointLocation::SetIgnoreCount (int32_t n) +{ + GetLocationOptions()->SetIgnoreCount(n); +} + +BreakpointOptions * +BreakpointLocation::GetOptionsNoCopy () +{ + if (m_options_ap.get() != NULL) + return m_options_ap.get(); + else + return m_owner.GetOptions (); +} + +BreakpointOptions * +BreakpointLocation::GetLocationOptions () +{ + if (m_options_ap.get() == NULL) + m_options_ap.reset(new BreakpointOptions (*m_owner.GetOptions ())); + + return m_options_ap.get(); +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool +BreakpointLocation::ShouldStop (StoppointCallbackContext *context) +{ + bool should_stop = true; + + m_hit_count++; + + if (!IsEnabled()) + return false; + + if (GetThreadID() != LLDB_INVALID_THREAD_ID + && context->context.thread->GetID() != GetThreadID()) + return false; + + if (m_hit_count <= GetIgnoreCount()) + return false; + + // Tell if the callback is synchronous here. + context->is_synchronous = true; + should_stop = InvokeCallback (context); + + if (should_stop) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + { + StreamString s; + GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Hit breakpoint location: %s\n", s.GetData()); + } + } + return should_stop; +} + +bool +BreakpointLocation::IsResolved () const +{ + return m_bp_site_sp.get() != NULL; +} + +bool +BreakpointLocation::ResolveBreakpointSite () +{ + if (m_bp_site_sp) + return true; + + Process* process = m_owner.GetTarget().GetProcessSP().get(); + if (process == NULL) + return false; + + BreakpointLocationSP myself_sp(m_owner.GetLocationSP (this)); + + lldb::user_id_t new_id = process->CreateBreakpointSite (myself_sp, false); + + if (new_id == LLDB_INVALID_UID) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Warning ("Tried to add breakpoint site at 0x%llx but it was already present.\n", + m_address.GetLoadAddress(process)); + 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; + s->Indent(); + BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID()); + + if (level == lldb::eDescriptionLevelBrief) + return; + + s->PutCString(": "); + + if (level == lldb::eDescriptionLevelVerbose) + s->IndentMore(); + + if (m_address.IsSectionOffset()) + { + m_address.CalculateSymbolContext(&sc); + + if (level == lldb::eDescriptionLevelFull) + { + s->PutCString("where = "); + sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address); + } + 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 = "); + dynamic_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); + } + + } + 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(); + } + s->Printf ("%saddress = ", (level == lldb::eDescriptionLevelFull && m_address.IsSectionOffset()) ? ", " : ""); + ExecutionContextScope *exe_scope = NULL; + Target *target = &m_owner.GetTarget(); + if (target) + exe_scope = target->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target; + + 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("enabled = %s\n", IsEnabled() ? "true" : "false"); + + s->Indent(); + s->Printf ("hit count = %-4u\n", GetHitCount()); + + if (m_options_ap.get()) + { + Baton *baton = m_options_ap->GetBaton(); + if (baton) + { + s->Indent(); + baton->GetDescription (s, level); + s->EOL(); + } + } + s->IndentLess(); + } + else + { + s->Printf(", %sresolved, %s, hit count = %u", + (IsResolved() ? "" : "un"), + (IsEnabled() ? "enabled" : "disabled"), + GetHitCount()); + } +} + +void +BreakpointLocation::Dump(Stream *s) const +{ + if (s == NULL) + return; + + s->Printf("BreakpointLocation %u: tid = %4.4x load addr = 0x%8.8llx state = %s type = %s breakpoint hw_index = %i hit_count = %-4u ignore_count = %-4u", + GetID(), + m_tid, + (uint64_t) m_address.GetLoadAddress(m_owner.GetTarget().GetProcessSP().get()), + (m_options_ap.get() ? m_options_ap->IsEnabled() : m_owner.IsEnabled()) ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + GetHardwareIndex(), + GetHitCount(), + m_options_ap.get() ? m_options_ap->GetIgnoreCount() : m_owner.GetIgnoreCount()); +} diff --git a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp new file mode 100644 index 000000000000..7b574263003d --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp @@ -0,0 +1,161 @@ +//===-- 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/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointLocationList.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::user_id_t bp_id, lldb::user_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::user_id_t break_id, lldb::user_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::user_id_t m_break_id; + const lldb::user_id_t m_break_loc_id; +}; + +BreakpointLocationCollection::collection::iterator +BreakpointLocationCollection::GetIDPairIterator (lldb::user_id_t break_id, lldb::user_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::user_id_t break_id, lldb::user_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::user_id_t break_id, lldb::user_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::user_id_t break_id, lldb::user_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 (uint32_t i) +{ + BreakpointLocationSP stop_sp; + if (i >= 0 && i < m_break_loc_collection.size()) + stop_sp = m_break_loc_collection[i]; + + return stop_sp; +} + +const BreakpointLocationSP +BreakpointLocationCollection::GetByIndex (uint32_t i) const +{ + BreakpointLocationSP stop_sp; + if (i >= 0 && i < m_break_loc_collection.size()) + stop_sp = m_break_loc_collection[i]; + + return stop_sp; +} + +bool +BreakpointLocationCollection::ShouldStop (StoppointCallbackContext *context) +{ + bool shouldStop = false; + + for (int i = 0; i < GetSize(); i++) { + bool one_result = GetByIndex(i)->ShouldStop(context); + if (one_result) + shouldStop = true; + } + return shouldStop; +} + +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/lldb/source/Breakpoint/BreakpointLocationList.cpp b/lldb/source/Breakpoint/BreakpointLocationList.cpp new file mode 100644 index 000000000000..d86a8cf4c473 --- /dev/null +++ b/lldb/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/Core/ModuleList.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocationList::BreakpointLocationList() : + m_locations(), + m_address_to_location (), + m_mutex (Mutex::eMutexTypeRecursive), + m_next_id (0) +{ +} + +BreakpointLocationList::~BreakpointLocationList() +{ +} + +lldb::user_id_t +BreakpointLocationList::Add (BreakpointLocationSP &bp_loc_sp) +{ + if (bp_loc_sp) + { + Mutex::Locker locker (m_mutex); + m_locations.push_back (bp_loc_sp); + m_address_to_location[bp_loc_sp->GetAddress()] = bp_loc_sp; + return bp_loc_sp->GetID(); + } + return LLDB_INVALID_BREAK_ID; +} + +bool +BreakpointLocationList::ShouldStop (StoppointCallbackContext *context, lldb::user_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::user_id_t +BreakpointLocationList::FindIDByAddress (Address &addr) +{ + BreakpointLocationSP bp_loc_sp = FindByAddress (addr); + if (bp_loc_sp) + { + return bp_loc_sp->GetID(); + } + return LLDB_INVALID_BREAK_ID; +} + +bool +BreakpointLocationList::Remove (lldb::user_id_t break_id) +{ + Mutex::Locker locker (m_mutex); + collection::iterator pos = GetIDIterator(break_id); // Predicate + if (pos != m_locations.end()) + { + m_address_to_location.erase ((*pos)->GetAddress()); + m_locations.erase(pos); + return true; + } + return false; +} + + +class BreakpointLocationIDMatches +{ +public: + BreakpointLocationIDMatches (lldb::user_id_t break_id) : + m_break_id(break_id) + { + } + + bool operator() (const BreakpointLocationSP &bp_loc_sp) const + { + return m_break_id == bp_loc_sp->GetID(); + } + +private: + const lldb::user_id_t m_break_id; +}; + +class BreakpointLocationAddressMatches +{ +public: + BreakpointLocationAddressMatches (Address& addr) : + m_addr(addr) + { + } + + bool operator() (const BreakpointLocationSP& bp_loc_sp) const + { + return Address::CompareFileAddress(m_addr, bp_loc_sp->GetAddress()) == 0; + } + +private: + const Address &m_addr; +}; + +BreakpointLocationList::collection::iterator +BreakpointLocationList::GetIDIterator (lldb::user_id_t break_id) +{ + Mutex::Locker locker (m_mutex); + return std::find_if (m_locations.begin(), + m_locations.end(), + BreakpointLocationIDMatches(break_id)); +} + +BreakpointLocationList::collection::const_iterator +BreakpointLocationList::GetIDConstIterator (lldb::user_id_t break_id) const +{ + Mutex::Locker locker (m_mutex); + return std::find_if (m_locations.begin(), + m_locations.end(), + BreakpointLocationIDMatches(break_id)); +} + +BreakpointLocationSP +BreakpointLocationList::FindByID (lldb::user_id_t break_id) +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP stop_sp; + collection::iterator pos = GetIDIterator(break_id); + if (pos != m_locations.end()) + stop_sp = *pos; + + return stop_sp; +} + +const BreakpointLocationSP +BreakpointLocationList::FindByID (lldb::user_id_t break_id) const +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP stop_sp; + collection::const_iterator pos = GetIDConstIterator(break_id); + if (pos != m_locations.end()) + stop_sp = *pos; + + return stop_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) + { + bool seen = false; + BreakpointLocationSP break_loc = (*pos); + const Section *section = break_loc->GetAddress().GetSection(); + if (section) + { + if (section->GetModule() == module) + { + if (!seen) + { + seen = true; + bp_loc_list.Add (break_loc); + } + + } + } + } + return bp_loc_list.GetSize() - orig_size; +} + + +static int +FindLocationByAddress (Address *addr_ptr, const BreakpointLocationSP *bp_loc_sp_ptr) +{ + return Address::CompareModulePointerAndOffset(*addr_ptr, (*bp_loc_sp_ptr)->GetAddress()); +} + +const BreakpointLocationSP +BreakpointLocationList::FindByAddress (Address &addr) const +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP stop_sp; + if (!m_locations.empty()) + { + addr_map::const_iterator pos = m_address_to_location.find (addr); + if (pos != m_address_to_location.end()) + stop_sp = pos->second; + } + + return stop_sp; +} + +void +BreakpointLocationList::Dump (Stream *s) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + Mutex::Locker locker (m_mutex); + s->Printf("BreakpointLocationList with %zu BreakpointLocations:\n", 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 (uint32_t i) +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP stop_sp; + if (i >= 0 && i < m_locations.size()) + stop_sp = m_locations[i]; + + return stop_sp; +} + +const BreakpointLocationSP +BreakpointLocationList::GetByIndex (uint32_t i) const +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP stop_sp; + if (i >= 0 && i < m_locations.size()) + stop_sp = m_locations[i]; + + return stop_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) + (*pos)->ResolveBreakpointSite(); +} + +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; +} + +break_id_t +BreakpointLocationList::GetNextID() +{ + return ++m_next_id; +} + +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); + } +} + diff --git a/lldb/source/Breakpoint/BreakpointOptions.cpp b/lldb/source/Breakpoint/BreakpointOptions.cpp new file mode 100644 index 000000000000..4f664c4692fe --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -0,0 +1,180 @@ +//===-- 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/Breakpoint/StoppointCallbackContext.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_is_synchronous (false), + m_callback_baton_sp (), + m_enabled (true), + m_ignore_count (0), + m_thread_id (LLDB_INVALID_THREAD_ID) +{ +} + +//---------------------------------------------------------------------- +// 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_ignore_count (rhs.m_ignore_count), + m_thread_id (rhs.m_thread_id) +{ +} + +//---------------------------------------------------------------------- +// 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_ignore_count = rhs.m_ignore_count; + m_thread_id = rhs.m_thread_id; + return *this; +} + +//---------------------------------------------------------------------- +// 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 = NULL; + m_callback_baton_sp.reset(); +} + +Baton * +BreakpointOptions::GetBaton () +{ + 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; +} + +//------------------------------------------------------------------ +// Enabled/Ignore Count +//------------------------------------------------------------------ +bool +BreakpointOptions::IsEnabled () const +{ + return m_enabled; +} + +void +BreakpointOptions::SetEnabled (bool enabled) +{ + m_enabled = enabled; +} + +int32_t +BreakpointOptions::GetIgnoreCount () const +{ + return m_ignore_count; +} + +void +BreakpointOptions::SetIgnoreCount (int32_t n) +{ + m_ignore_count = n; +} + +void +BreakpointOptions::SetThreadID (lldb::tid_t thread_id) +{ + m_thread_id = thread_id; +} + +lldb::tid_t +BreakpointOptions::GetThreadID () const +{ + return m_thread_id; +} + + + +void +BreakpointOptions::CommandBaton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + s->Indent("Breakpoint commands:\n"); + CommandData *data = (CommandData *)m_data; + + 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 (); +} + diff --git a/lldb/source/Breakpoint/BreakpointResolver.cpp b/lldb/source/Breakpoint/BreakpointResolver.cpp new file mode 100644 index 000000000000..e0fbfde79326 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointResolver.cpp @@ -0,0 +1,60 @@ +//===-- 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) : + m_breakpoint (bkpt) +{ +} + +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/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/lldb/source/Breakpoint/BreakpointResolverAddress.cpp new file mode 100644 index 000000000000..034ef601dc4d --- /dev/null +++ b/lldb/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), + 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 breakpoint: "); + m_addr.Dump(s, m_breakpoint->GetTarget().GetProcessSP().get(), Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); +} + +void +BreakpointResolverAddress::Dump (Stream *s) const +{ + +} diff --git a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp new file mode 100644 index 000000000000..86e059644859 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -0,0 +1,122 @@ +//===-- 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/StreamString.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 +) : + BreakpointResolver (bkpt), + m_file_spec (file_spec), + m_line_number (line_no), + m_inlines (check_inlines) +{ +} + +BreakpointResolverFileLine::~BreakpointResolverFileLine () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileLine::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList sc_list; + uint32_t sc_list_size; + CompileUnit *cu = context.comp_unit; + + assert (m_breakpoint != NULL); + 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 (int i = 0; i < sc_list_size; i++) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(i, sc)) + { + Address line_start = sc.line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + 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 ("error: Unable to set breakpoint at file address 0x%llx for %s:%d\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString(""), + m_line_number); + } + } + else + { +#if 0 + s << "error: Breakpoint at '" << pos->c_str() << "' isn't resolved yet: \n"; + if (sc.line_entry.address.Dump(&s, Address::DumpStyleSectionNameOffset)) + s.EOL(); + if (sc.line_entry.address.Dump(&s, Address::DumpStyleSectionPointerOffset)) + s.EOL(); + if (sc.line_entry.address.Dump(&s, Address::DumpStyleFileAddress)) + s.EOL(); + if (sc.line_entry.address.Dump(&s, Address::DumpStyleLoadAddress)) + s.EOL(); +#endif + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileLine::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +BreakpointResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("File and line breakpoint - file: \"%s\" line: %u", m_file_spec.GetFilename().AsCString(), m_line_number); +} + +void +BreakpointResolverFileLine::Dump (Stream *s) const +{ + +} + diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp b/lldb/source/Breakpoint/BreakpointResolverName.cpp new file mode 100644 index 000000000000..b04bb8aef483 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp @@ -0,0 +1,252 @@ +//===-- 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/StreamString.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointResolverName::BreakpointResolverName +( + Breakpoint *bkpt, + const char *func_name, + Breakpoint::MatchType type +) : + BreakpointResolver (bkpt), + m_func_name (func_name), + m_class_name (NULL), + m_regex (), + m_match_type (type) +{ + if (m_match_type == Breakpoint::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()); + } + } +} + +BreakpointResolverName::BreakpointResolverName +( + Breakpoint *bkpt, + RegularExpression &func_regex +) : + BreakpointResolver (bkpt), + m_func_name (NULL), + m_class_name (NULL), + m_regex (func_regex), + m_match_type (Breakpoint::Regexp) +{ + +} + +BreakpointResolverName::BreakpointResolverName +( + Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type +) : + BreakpointResolver (bkpt), + m_func_name (method), + m_class_name (class_name), + m_regex (), + m_match_type (type) +{ + +} + +BreakpointResolverName::~BreakpointResolverName () +{ +} + +// 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; + + bool skip_prologue = true; + uint32_t i; + bool new_location; + SymbolContext sc; + 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; + } + + switch (m_match_type) + { + case Breakpoint::Exact: + if (context.module_sp) + { + context.module_sp->FindSymbolsWithNameAndType (m_func_name, eSymbolTypeCode, sym_list); + context.module_sp->FindFunctions (m_func_name, false, func_list); + } + break; + case Breakpoint::Regexp: + if (context.module_sp) + { + context.module_sp->FindSymbolsMatchingRegExAndType (m_regex, eSymbolTypeCode, sym_list); + context.module_sp->FindFunctions (m_regex, true, func_list); + } + break; + case Breakpoint::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->GetAddressRangePtr()) + { + if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddressRangePtr()->GetBaseAddress()) + { + 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) + { + break_addr = sc.function->GetAddressRange().GetBaseAddress(); + if (skip_prologue) + { + const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize(); + if (prologue_byte_size) + break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); + } + + 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()); + } + } + } + } + } + } + } + + for (i = 0; i < sym_list.GetSize(); i++) + { + if (sym_list.GetContextAtIndex(i, sc)) + { + if (sc.symbol && sc.symbol->GetAddressRangePtr()) + { + break_addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress(); + + if (skip_prologue) + { + const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize(); + if (prologue_byte_size) + break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); + } + + 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()) + { + StreamString s; + bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + if (log) + log->Printf ("Added location: %s\n", s.GetData()); + } + } + } + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverName::GetDescription (Stream *s) +{ + s->PutCString("Breakpoint by name: "); + + if (m_match_type == Breakpoint::Regexp) + s->Printf("'%s' (regular expression)", m_regex.GetText()); + else + s->Printf("'%s'", m_func_name.AsCString()); +} + +void +BreakpointResolverName::Dump (Stream *s) const +{ + +} + diff --git a/lldb/source/Breakpoint/BreakpointSite.cpp b/lldb/source/Breakpoint/BreakpointSite.cpp new file mode 100644 index 000000000000..cd0920d07c75 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointSite.cpp @@ -0,0 +1,221 @@ +//===-- 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/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSiteList.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointSite::BreakpointSite +( + BreakpointSiteList *list, + BreakpointLocationSP& owner, + lldb::addr_t addr, + lldb::tid_t tid, + bool use_hardware +) : + StoppointLocation(GetNextID(), addr, tid, 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; + for (int i = 0; i < m_owners.GetSize(); 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) +{ + m_hit_count++; + return m_owners.ShouldStop (context); +} + +bool +BreakpointSite::IsBreakpointAtThisSite (lldb::break_id_t bp_id) +{ + for (int i = 0; i < m_owners.GetSize(); 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: tid = %4.4x addr = 0x%8.8llx type = %s breakpoint hw_index = %i hit_count = %-4u", + GetID(), + m_tid, + (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 ", GetID()); + m_owners.GetDescription (s, level); +} + +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, size_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 (BreakpointLocationSP &owner) +{ + m_owners.Add(owner); +} + +uint32_t +BreakpointSite::RemoveOwner (lldb::user_id_t break_id, lldb::user_id_t break_loc_id) +{ + m_owners.Remove(break_id, break_loc_id); + return m_owners.GetSize(); +} + +uint32_t +BreakpointSite::GetNumberOfOwners () +{ + return m_owners.GetSize(); +} + +BreakpointLocationSP +BreakpointSite::GetOwnerAtIndex (uint32_t index) +{ + return m_owners.GetByIndex (index); +} + +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/lldb/source/Breakpoint/BreakpointSiteList.cpp b/lldb/source/Breakpoint/BreakpointSiteList.cpp new file mode 100644 index 000000000000..19e18bb749a0 --- /dev/null +++ b/lldb/source/Breakpoint/BreakpointSiteList.cpp @@ -0,0 +1,229 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +BreakpointSiteList::BreakpointSiteList() : + 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::user_id_t +BreakpointSiteList::Add(const BreakpointSiteSP &bp) +{ + lldb::addr_t bp_site_load_addr = bp->GetLoadAddress(); + 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::user_id_t break_id) +{ + BreakpointSiteSP bp = FindByID (break_id); + if (bp) + { + // 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 bp->ShouldStop (context); + } + // We should stop here since this BreakpointSite isn't valid anymore or it + // doesn't exist. + return true; +} +lldb::user_id_t +BreakpointSiteList::FindIDByAddress (lldb::addr_t addr) +{ + BreakpointSiteSP bp = FindByAddress (addr); + if (bp) + { + //DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8llx ) => %u", __FUNCTION__, (uint64_t)addr, bp->GetID()); + return bp.get()->GetID(); + } + //DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8llx ) => NONE", __FUNCTION__, (uint64_t)addr); + return LLDB_INVALID_BREAK_ID; +} + +bool +BreakpointSiteList::Remove (lldb::user_id_t break_id) +{ + 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) +{ + 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::user_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::user_id_t m_break_id; +}; + +BreakpointSiteList::collection::iterator +BreakpointSiteList::GetIDIterator (lldb::user_id_t break_id) +{ + 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::user_id_t break_id) const +{ + 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::user_id_t break_id) +{ + 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::user_id_t break_id) const +{ + 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; + + collection::iterator iter = m_bp_site_list.find(addr); + if (iter != m_bp_site_list.end()) + found_sp = iter->second; + return found_sp; +} + +void +BreakpointSiteList::Dump (Stream *s) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, 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(); +} + + +BreakpointSiteSP +BreakpointSiteList::GetByIndex (uint32_t i) +{ + BreakpointSiteSP stop_sp; + collection::iterator end = m_bp_site_list.end(); + collection::iterator pos; + uint32_t curr_i = 0; + for (pos = m_bp_site_list.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = pos->second; + } + return stop_sp; +} + +const BreakpointSiteSP +BreakpointSiteList::GetByIndex (uint32_t i) const +{ + BreakpointSiteSP stop_sp; + collection::const_iterator end = m_bp_site_list.end(); + collection::const_iterator pos; + uint32_t curr_i = 0; + for (pos = m_bp_site_list.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = pos->second; + } + return stop_sp; +} + +void +BreakpointSiteList::SetEnabledForAll (const bool enabled, const lldb::user_id_t except_id) +{ + collection::iterator end = m_bp_site_list.end(); + collection::iterator pos; + for (pos = m_bp_site_list.begin(); pos != end; ++pos) + { + if (except_id != LLDB_INVALID_BREAK_ID && except_id != pos->second->GetID()) + pos->second->SetEnabled (enabled); + else + pos->second->SetEnabled (!enabled); + } +} + +const BreakpointSiteList::collection * +BreakpointSiteList::GetMap () +{ + return &m_bp_site_list; +} diff --git a/lldb/source/Breakpoint/Stoppoint.cpp b/lldb/source/Breakpoint/Stoppoint.cpp new file mode 100644 index 000000000000..583ab47005f5 --- /dev/null +++ b/lldb/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/lldb/source/Breakpoint/StoppointCallbackContext.cpp b/lldb/source/Breakpoint/StoppointCallbackContext.cpp new file mode 100644 index 000000000000..86620621c768 --- /dev/null +++ b/lldb/source/Breakpoint/StoppointCallbackContext.cpp @@ -0,0 +1,38 @@ +//===-- 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), + context() +{ +} + +StoppointCallbackContext::StoppointCallbackContext(Event *e, Process* p, Thread *t, StackFrame *f, bool synchronously) : + event (e), + context (p, t, f), + is_synchronous(synchronously) +{ +} + +void +StoppointCallbackContext::Clear() +{ + event = NULL; + context.Clear(); + is_synchronous = false; +} diff --git a/lldb/source/Breakpoint/StoppointLocation.cpp b/lldb/source/Breakpoint/StoppointLocation.cpp new file mode 100644 index 000000000000..999ad536ab8c --- /dev/null +++ b/lldb/source/Breakpoint/StoppointLocation.cpp @@ -0,0 +1,120 @@ +//===-- 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, tid_t tid, bool hardware) : + m_loc_id(bid), + m_tid(tid), + m_byte_size(0), + m_addr(addr), + m_hit_count(0), + m_hw_preferred(hardware), + m_hw_index(LLDB_INVALID_INDEX32) +{ +} + +StoppointLocation::StoppointLocation (break_id_t bid, addr_t addr, tid_t tid, size_t size, bool hardware) : + m_loc_id(bid), + m_tid(tid), + m_byte_size(size), + m_addr(addr), + m_hit_count(0), + m_hw_preferred(hardware), + m_hw_index(LLDB_INVALID_INDEX32) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StoppointLocation::~StoppointLocation() +{ +} + + +size_t +StoppointLocation::GetByteSize () const +{ + return m_byte_size; +} + +addr_t +StoppointLocation::GetLoadAddress() const +{ + return m_addr; +} + +tid_t +StoppointLocation::GetThreadID() const +{ + return m_tid; +} + +uint32_t +StoppointLocation::GetHitCount () const +{ + return m_hit_count; +} + +bool +StoppointLocation::HardwarePreferred () const +{ + return m_hw_preferred; +} + +bool +StoppointLocation::IsHardware () const +{ + return m_hw_index != LLDB_INVALID_INDEX32; +} + +uint32_t +StoppointLocation::GetHardwareIndex () const +{ + return m_hw_index; +} + +void +StoppointLocation::SetHardwareIndex (uint32_t hw_index) +{ + m_hw_index = hw_index; +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool +StoppointLocation::ShouldStop (StoppointCallbackContext *context) +{ + return true; +} + +break_id_t +StoppointLocation::GetID() const +{ + return m_loc_id; +} + +void +StoppointLocation::Dump (Stream *stream) const +{ + +} diff --git a/lldb/source/Breakpoint/WatchpointLocation.cpp b/lldb/source/Breakpoint/WatchpointLocation.cpp new file mode 100644 index 000000000000..f765ef432029 --- /dev/null +++ b/lldb/source/Breakpoint/WatchpointLocation.cpp @@ -0,0 +1,137 @@ +//===-- WatchpointLocation.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/WatchpointLocation.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +WatchpointLocation::WatchpointLocation (lldb::addr_t addr, lldb::tid_t tid, bool hardware) : + StoppointLocation (GetNextID(), addr, tid, hardware), + m_enabled(0), + m_watch_read(0), + m_watch_write(0), + m_watch_was_read(0), + m_watch_was_written(0), + m_ignore_count(0), + m_callback(NULL), + m_callback_baton(NULL) +{ +} + +WatchpointLocation::~WatchpointLocation() +{ +} + +break_id_t +WatchpointLocation::GetNextID() +{ + static break_id_t g_next_ID = 0; + return ++g_next_ID; +} + +bool +WatchpointLocation::SetCallback (WatchpointHitCallback callback, void *callback_baton) +{ + m_callback = callback; + m_callback_baton = callback_baton; + return true; +} + + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool +WatchpointLocation::BreakpointWasHit (StoppointCallbackContext *context) +{ + m_hit_count++; + + if (m_hit_count > m_ignore_count) + { + uint32_t access = 0; + if (m_watch_was_read) + access |= LLDB_WATCH_TYPE_READ; + if (m_watch_was_written) + access |= LLDB_WATCH_TYPE_WRITE; + return m_callback(m_callback_baton, context, GetID(), access); + } + return false; +} + +void +WatchpointLocation::Dump(Stream *s) const +{ + if (s == NULL) + return; + + s->Printf("WatchpointLocation %u: tid = %4.4x addr = 0x%8.8llx size = %zu state = %s type = %s watchpoint (%s%s) hw_index = %i hit_count = %-4u ignore_count = %-4u callback = %8p baton = %8p", + GetID(), + m_tid, + (uint64_t)m_addr, + m_byte_size, + m_enabled ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + m_watch_read ? "r" : "", + m_watch_write ? "w" : "", + GetHardwareIndex(), + GetHitCount(), + GetIgnoreCount(), + m_callback, + m_callback_baton); +} + +bool +WatchpointLocation::IsEnabled() const +{ + return m_enabled; +} + +void +WatchpointLocation::SetEnabled(uint32_t enabled) +{ + if (!enabled) + SetHardwareIndex(LLDB_INVALID_INDEX32); + m_enabled = enabled; +} + +void +WatchpointLocation::SetWatchpointType (uint32_t type) +{ + m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0; + m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0; +} + +bool +WatchpointLocation::WatchpointRead () const +{ + return m_watch_read != 0; +} +bool +WatchpointLocation::WatchpointWrite () const +{ + return m_watch_write != 0; +} +int32_t +WatchpointLocation::GetIgnoreCount () const +{ + return m_ignore_count; +} + +void +WatchpointLocation::SetIgnoreCount (int32_t n) +{ + m_ignore_count = n; +} diff --git a/lldb/source/Commands/CommandObjectAdd.cpp b/lldb/source/Commands/CommandObjectAdd.cpp new file mode 100644 index 000000000000..cd1d02d22ffd --- /dev/null +++ b/lldb/source/Commands/CommandObjectAdd.cpp @@ -0,0 +1,51 @@ +//===-- CommandObjectAdd.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectAdd.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectAdd +//------------------------------------------------------------------------- + +CommandObjectAdd::CommandObjectAdd () : + CommandObject ("add", + "Allows the user to add a new command/function pair to the debugger's dictionary.", + "add ") +{ +} + +CommandObjectAdd::~CommandObjectAdd() +{ +} + + +bool +CommandObjectAdd::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + result.AppendMessage ("This function has not been implemented yet."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); +} diff --git a/lldb/source/Commands/CommandObjectAdd.h b/lldb/source/Commands/CommandObjectAdd.h new file mode 100644 index 000000000000..9aa59b61a7ad --- /dev/null +++ b/lldb/source/Commands/CommandObjectAdd.h @@ -0,0 +1,43 @@ +//===-- CommandObjectAdd.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectAdd_h_ +#define liblldb_CommandObjectAdd_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { +//------------------------------------------------------------------------- +// CommandObjectAdd +//------------------------------------------------------------------------- + +class CommandObjectAdd : public CommandObject +{ +public: + + CommandObjectAdd (); + + virtual + ~CommandObjectAdd (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectAdd_h_ diff --git a/lldb/source/Commands/CommandObjectAlias.cpp b/lldb/source/Commands/CommandObjectAlias.cpp new file mode 100644 index 000000000000..50c2ab92e9f7 --- /dev/null +++ b/lldb/source/Commands/CommandObjectAlias.cpp @@ -0,0 +1,225 @@ +//===-- CommandObjectAlias.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectAlias.h" + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Core/Args.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectAlias +//------------------------------------------------------------------------- + +CommandObjectAlias::CommandObjectAlias () : + CommandObject ("alias", + "Allows users to define their own debugger command abbreviations.", + "alias []") +{ + 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 'alias sc script' // Creates the abbreviation 'sc' for the 'script' \n\ + // command. \n\ + '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\ + 'alias bpi breakpoint list' // Creates the abbreviation 'bpi' 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\ + '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. So 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). So 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 (dbg) alias bfl breakpoint set -f %1 -l %2 \n\ + <... some time later ...> \n\ + (dbg) 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 (dbg) alias pltty process launch -s -o %1 -e %1 \n\ + (dbg) 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 alias bl3 breakpoint set -f %1 -l 3 // Always sets a breakpoint on line \n\ + // 3 of whatever file is indicated. \n"); + +} + +CommandObjectAlias::~CommandObjectAlias () +{ +} + + +bool +CommandObjectAlias::Execute (Args& args, CommandContext *context, CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + const int 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 (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(interpreter->GetCommandSP (actual_command.c_str())); + CommandObjectSP subcommand_obj_sp; + bool use_subcommand = false; + if (command_obj_sp.get()) + { + CommandObject *cmd_obj = command_obj_sp.get(); + CommandObject *sub_cmd_obj; + OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + if (cmd_obj->IsMultiwordObject()) + { + if (argc >= 3) + { + const std::string sub_command = args.GetArgumentAtIndex(0); + assert (sub_command.length() != 0); + subcommand_obj_sp = + (((CommandObjectMultiword *) 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. + } + else + { + result.AppendErrorWithFormat ("Error occurred while attempting to look up command '%s %s'.\n", + alias_command.c_str(), sub_command.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + // Verify & handle any options/arguments passed to the alias command + + if (args.GetArgumentCount () > 0) + { + if ((!use_subcommand && (cmd_obj->WantsRawCommandString())) + || (use_subcommand && (sub_cmd_obj->WantsRawCommandString()))) + { + result.AppendErrorWithFormat ("'%s' cannot be aliased with any options or arguments.\n", + (use_subcommand ? sub_cmd_obj->GetCommandName() + : cmd_obj->GetCommandName())); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // options or arguments have been passed to the alias command, and must be verified & processed here. + if ((!use_subcommand && (cmd_obj->GetOptions() != NULL)) + || (use_subcommand && (sub_cmd_obj->GetOptions() != NULL))) + { + Options *options; + if (use_subcommand) + options = sub_cmd_obj->GetOptions(); + else + options = cmd_obj->GetOptions(); + options->ResetOptionValues (); + args.Unshift ("dummy_arg"); + args.ParseAliasOptions (*options, result, option_arg_vector); + args.Shift (); + if (result.Succeeded()) + options->VerifyPartialOptions (result); + if (!result.Succeeded()) + return false; + } + else + { + for (int i = 0; i < args.GetArgumentCount(); ++i) + option_arg_vector->push_back (OptionArgPair ("", + std::string (args.GetArgumentAtIndex (i)))); + } + } + + // Create the alias. + + if (interpreter->AliasExists (alias_command.c_str()) + || interpreter->UserCommandExists (alias_command.c_str())) + { + OptionArgVectorSP tmp_option_arg_sp (interpreter->GetAliasOptions (alias_command.c_str())); + if (tmp_option_arg_sp.get()) + { + if (option_arg_vector->size() == 0) + interpreter->RemoveAliasOptions (alias_command.c_str()); + } + result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", alias_command.c_str()); + } + + if (use_subcommand) + interpreter->AddAlias (alias_command.c_str(), subcommand_obj_sp); + else + interpreter->AddAlias (alias_command.c_str(), command_obj_sp); + if (option_arg_vector->size() > 0) + 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 result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectAlias.h b/lldb/source/Commands/CommandObjectAlias.h new file mode 100644 index 000000000000..859b7cea0497 --- /dev/null +++ b/lldb/source/Commands/CommandObjectAlias.h @@ -0,0 +1,43 @@ +//===-- CommandObjectAlias.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectAlias_h_ +#define liblldb_CommandObjectAlias_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectAlias +//------------------------------------------------------------------------- + +class CommandObjectAlias : public CommandObject +{ +public: + + CommandObjectAlias (); + + virtual + ~CommandObjectAlias (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectAlias_h_ diff --git a/lldb/source/Commands/CommandObjectAppend.cpp b/lldb/source/Commands/CommandObjectAppend.cpp new file mode 100644 index 000000000000..613b85b4be9b --- /dev/null +++ b/lldb/source/Commands/CommandObjectAppend.cpp @@ -0,0 +1,95 @@ +//===-- CommandObjectAppend.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectAppend.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; + +//----------------------------------------------------------------------------- +// CommandObjectAppend +//----------------------------------------------------------------------------- + +CommandObjectAppend::CommandObjectAppend () : + CommandObject ("append", + "Allows the user to append a value to a single debugger setting variable, for settings that are of list types. Type 'settings' to see a list of debugger setting variables", + "append ") +{ +} + +CommandObjectAppend::~CommandObjectAppend () +{ +} + +bool +CommandObjectAppend::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandInterpreter::VariableMap::iterator pos; + + const int argc = command.GetArgumentCount(); + if (argc < 2) + { + result.AppendError ("'append' requires at least two arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = command.GetArgumentAtIndex(0); + command.Shift(); + + + if (var_name == NULL || var_name[0] == '\0') + { + result.AppendError ("'set' command requires a valid variable name. No value supplied"); + result.SetStatus (eReturnStatusFailed); + } + else + { + StateVariable *var = interpreter->GetStateVariable(var_name); + if (var == NULL) + { + result.AppendErrorWithFormat ("'%s' is not a settable internal variable.\n", var_name); + result.SetStatus (eReturnStatusFailed); + } + else + { + if (var->GetType() == StateVariable::eTypeString) + { + for (int i = 0; i < command.GetArgumentCount(); ++i) + var->AppendStringValue (command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else if (var->GetType() == StateVariable::eTypeStringArray) + { + var->GetArgs().AppendArguments (command); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("Values cannot be appended to variable '%s'. Try 'set' instead.\n", var_name); + result.SetStatus (eReturnStatusFailed); + } + } + } + return result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectAppend.h b/lldb/source/Commands/CommandObjectAppend.h new file mode 100644 index 000000000000..436283730fb9 --- /dev/null +++ b/lldb/source/Commands/CommandObjectAppend.h @@ -0,0 +1,43 @@ +//===-- CommandObjectAppend.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectAppend_h_ +#define liblldb_CommandObjectAppend_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { +//----------------------------------------------------------------------------- +// CommandObjectAppend +//----------------------------------------------------------------------------- + +class CommandObjectAppend : public CommandObject +{ +public: + CommandObjectAppend (); + + virtual + ~CommandObjectAppend (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectAppend_h_ diff --git a/lldb/source/Commands/CommandObjectApropos.cpp b/lldb/source/Commands/CommandObjectApropos.cpp new file mode 100644 index 000000000000..0fcc093901fb --- /dev/null +++ b/lldb/source/Commands/CommandObjectApropos.cpp @@ -0,0 +1,96 @@ +//===-- 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 "CommandObjectApropos.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Options.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectApropos +//------------------------------------------------------------------------- + +CommandObjectApropos::CommandObjectApropos () : + CommandObject ("apropos", + "Finds a list of debugger commands related to a particular word/subject.", + "apropos ") +{ +} + +CommandObjectApropos::~CommandObjectApropos() +{ +} + + +bool +CommandObjectApropos::Execute (Args &command, CommandContext *context, CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + const int argc = command.GetArgumentCount (); + + if (argc == 1) + { + const char *search_word = command.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; + interpreter->FindCommandsForApropos (search_word, commands_found, commands_help); + if (commands_found.GetSize() == 0) + { + result.AppendMessageWithFormat ("No commands found pertaining to '%s'.", search_word); + result.AppendMessage ("Try 'help' to see a complete list of debugger commands."); + } + else + { + result.AppendMessageWithFormat ("The following commands may relate to '%s':\n", search_word); + size_t max_len = 0; + + for (int i = 0; i < commands_found.GetSize(); ++i) + { + int len = strlen (commands_found.GetStringAtIndex (i)); + if (len > max_len) + max_len = len; + } + + for (int i = 0; i < commands_found.GetSize(); ++i) + interpreter->OutputFormattedHelpText (result.GetOutputStream(), commands_found.GetStringAtIndex(i), + "--", commands_help.GetStringAtIndex(i), max_len); + + } + 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/lldb/source/Commands/CommandObjectApropos.h b/lldb/source/Commands/CommandObjectApropos.h new file mode 100644 index 000000000000..a079a3fc4acd --- /dev/null +++ b/lldb/source/Commands/CommandObjectApropos.h @@ -0,0 +1,45 @@ +//===-- 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 CommandObject +{ +public: + + CommandObjectApropos (); + + virtual + ~CommandObjectApropos (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectApropos_h_ diff --git a/lldb/source/Commands/CommandObjectArgs.cpp b/lldb/source/Commands/CommandObjectArgs.cpp new file mode 100644 index 000000000000..7f1fd3dda940 --- /dev/null +++ b/lldb/source/Commands/CommandObjectArgs.cpp @@ -0,0 +1,279 @@ +//===-- 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 "CommandObjectArgs.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.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/CommandContext.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 () : +Options() +{ + // Keep only one place to reset the values to their defaults + ResetOptionValues(); +} + + +CommandObjectArgs::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectArgs::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectArgs::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); +} + +const lldb::OptionDefinition* +CommandObjectArgs::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectArgs::CommandObjectArgs () : + CommandObject ("args", + "When stopped at the start of a function, reads function arguments of type (u?)int(8|16|32|64)_t, (void|char)*", + "args") +{ +} + +CommandObjectArgs::~CommandObjectArgs () +{ +} + +Options * +CommandObjectArgs::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectArgs::Execute(Args &command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + ConstString target_triple; + + Process *process = context->GetExecutionContext().process; + if (!process) + { + result.AppendError ("Args found no process."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const ABI *abi = process->GetABI (); + if (!abi) + { + result.AppendError ("The current process has no ABI."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + int num_args = command.GetArgumentCount (); + int arg_index; + + if (!num_args) + { + result.AppendError ("args requires at least one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Thread *thread = context->GetExecutionContext ().thread; + + if (!thread) + { + result.AppendError ("args found no thread."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + lldb::StackFrameSP thread_cur_frame = thread->GetCurrentFrame (); + if (!thread_cur_frame) + { + result.AppendError ("The current thread has no current frame."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Module *thread_module = thread_cur_frame->GetPC ().GetModule (); + if (!thread_module) + { + result.AppendError ("The PC has no associated module."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + TypeList *thread_type_list = thread_module->GetTypeList (); + if (!thread_type_list) + { + result.AppendError ("The module has no type list."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ClangASTContext &ast_context = thread_type_list->GetClangASTContext(); + + ValueList value_list; + + for (arg_index = 0; arg_index < num_args; ++arg_index) + { + const char *arg_type_cstr = command.GetArgumentAtIndex(arg_index); + Value value; + value.SetValueType(Value::eValueTypeScalar); + void *type; + + char *int_pos; + if ((int_pos = strstr (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; + } + + type = ast_context.GetBuiltinTypeForEncodingAndBitSize(encoding, width); + + if (!type) + { + 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*")) + type = ast_context.CreatePointerType (ast_context.GetVoidBuiltInType ()); + else if (!strcmp (arg_type_cstr, "char*")) + 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.SetContext (Value::eContextTypeOpaqueClangQualType, 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 ("%d (%s): ", arg_index, command.GetArgumentAtIndex (arg_index)); + value_list.GetValueAtIndex (arg_index)->Dump (&result.GetOutputStream ()); + result.GetOutputStream ().Printf("\n"); + } + + return result.Succeeded(); +} + +lldb::OptionDefinition +CommandObjectArgs::CommandOptions::g_option_table[] = +{ + { 0, false, "debug", 'g', no_argument, NULL, 0, NULL, "Enable verbose debug logging of the expression parsing and evaluation."}, + { 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + diff --git a/lldb/source/Commands/CommandObjectArgs.h b/lldb/source/Commands/CommandObjectArgs.h new file mode 100644 index 000000000000..d326d4232476 --- /dev/null +++ b/lldb/source/Commands/CommandObjectArgs.h @@ -0,0 +1,76 @@ +//===-- 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/Core/Options.h" +#include "lldb/Core/Language.h" + +namespace lldb_private { + + class CommandObjectArgs : public CommandObject + { + public: + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + }; + + CommandObjectArgs (); + + virtual + ~CommandObjectArgs (); + + virtual + Options * + GetOptions (); + + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + WantsRawCommandString() { return false; } + + protected: + + CommandOptions m_options; + }; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectArgs_h_ diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp new file mode 100644 index 000000000000..d24ba8f553b6 --- /dev/null +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -0,0 +1,953 @@ +//===-- 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 "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/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" + +using namespace lldb; +using namespace lldb_private; + +static void +AddBreakpointDescription (CommandContext *context, StreamString *s, Breakpoint *bp, lldb::DescriptionLevel level) +{ + s->IndentMore(); + bp->GetDescription (s, level, true); + s->IndentLess(); + s->EOL(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointSet::CommandOptions +//------------------------------------------------------------------------- + +CommandObjectBreakpointSet::CommandOptions::CommandOptions() : + Options (), + m_filename (), + m_line_num (0), + m_column (0), + m_ignore_inlines (false), + m_func_name (), + m_func_regexp (), + m_modules (), + m_load_addr() +{ + BuildValidOptionSets(); +} + +CommandObjectBreakpointSet::CommandOptions::~CommandOptions () +{ +} + +lldb::OptionDefinition +CommandObjectBreakpointSet::CommandOptions::g_option_table[] = +{ + { 0, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, "", + "Set the breakpoint by source location in this particular file."}, + + { 0, true, "line", 'l', required_argument, NULL, 0, "", + "Set the breakpoint by source location at this particular line."}, + + { 0, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, "", + "Set the breakpoint only in this shared library (can use this option multiple times for multiple shlibs)."}, + + // 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."}, + + { 0, false, "ignore_inlines", 'i', no_argument, NULL, 0, NULL, + "Ignore inlined subroutines when setting the breakppoint." }, + + { 1, true, "address", 'a', required_argument, NULL, 0, "
", + "Set the breakpoint by address, at the specified address."}, + + { 1, false, "ignore_inlines", 'i', no_argument, NULL, 0, NULL, + "Ignore inlined subroutines when setting the breakppoint." }, + + { 2, true, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, "", + "Set the breakpoint by function name." }, + + { 2, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, "", + "Set the breakpoint only in this shared library (can use this option multiple times for multiple shlibs)."}, + + { 2, false, "ignore_inlines", 'i', no_argument, NULL, 0, NULL, + "Ignore inlined subroutines when setting the breakpoint." }, + + { 3, true, "func_regex", 'r', required_argument, NULL, 0, "", + "Set the breakpoint by function name, evaluating a regular-expression to find the function name(s)." }, + + { 3, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, "", + "Set the breakpoint only in this shared library (can use this option multiple times for multiple shlibs)."}, + + { 3, false, "ignore_inlines", 'i', no_argument, NULL, 0, NULL, + "Ignore inlined subroutines when setting the breakpoint." }, + + { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + +const lldb::OptionDefinition* +CommandObjectBreakpointSet::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +Error +CommandObjectBreakpointSet::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_load_addr = Args::StringToUInt64(optarg, LLDB_INVALID_ADDRESS, 0); + if (m_load_addr == LLDB_INVALID_ADDRESS) + m_load_addr = Args::StringToUInt64(optarg, LLDB_INVALID_ADDRESS, 16); + + if (m_load_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("Invalid address string '%s'.\n", optarg); + break; + + case 'c': + m_column = Args::StringToUInt32 (option_arg, 0); + break; + case 'f': + m_filename = option_arg; + break; + case 'i': + m_ignore_inlines = true; + break; + case 'l': + m_line_num = Args::StringToUInt32 (option_arg, 0); + break; + case 'n': + m_func_name = option_arg; + break; + case 'r': + m_func_regexp = option_arg; + break; + case 's': + { + m_modules.push_back (std::string (option_arg)); + break; + } + default: + error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectBreakpointSet::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + + m_filename.clear(); + m_line_num = 0; + m_column = 0; + m_ignore_inlines = false; + m_func_name.clear(); + m_func_regexp.clear(); + m_load_addr = LLDB_INVALID_ADDRESS; + m_modules.clear(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointSet +//------------------------------------------------------------------------- + +CommandObjectBreakpointSet::CommandObjectBreakpointSet () : + CommandObject ("breakpoint set", "Sets a breakpoint or set of breakpoints in the executable.", + "breakpoint set ") +{ +} + +CommandObjectBreakpointSet::~CommandObjectBreakpointSet () +{ +} + +Options * +CommandObjectBreakpointSet::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectBreakpointSet::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("Invalid target, set executable file using 'file' 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) + + 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_name.empty()) + break_type = eSetTypeFunctionName; + else if (!m_options.m_func_regexp.empty()) + break_type = eSetTypeFunctionRegexp; + + ModuleSP module_sp = target->GetExecutableModule(); + Breakpoint *bp = NULL; + FileSpec module; + bool use_module = false; + int num_modules = m_options.m_modules.size(); + + if ((num_modules > 0) && (break_type != eSetTypeAddress)) + use_module = true; + + switch (break_type) + { + case eSetTypeFileAndLine: // Breakpoint by source position + { + FileSpec file; + if (m_options.m_filename.empty()) + { + StackFrame *cur_frame = context->GetExecutionContext().frame; + if (cur_frame == NULL) + { + result.AppendError ("Attempting to set breakpoint by line number alone with no selected frame."); + result.SetStatus (eReturnStatusFailed); + break; + } + else if (!cur_frame->HasDebugInformation()) + { + result.AppendError ("Attempting to set breakpoint by line number alone but selected frame has no debug info."); + result.SetStatus (eReturnStatusFailed); + break; + } + else + { + const SymbolContext &context = cur_frame->GetSymbolContext(true); + if (context.line_entry.file) + { + file = context.line_entry.file; + } + else if (context.comp_unit != NULL) + { file = context.comp_unit; + } + else + { + result.AppendError ("Attempting to set breakpoint by line number alone but can't find the file for the selected frame."); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + else + { + file.SetFile(m_options.m_filename.c_str()); + } + + if (use_module) + { + for (int i = 0; i < num_modules; ++i) + { + module.SetFile(m_options.m_modules[i].c_str()); + bp = target->CreateBreakpoint (&module, + file, + m_options.m_line_num, + m_options.m_ignore_inlines).get(); + if (bp) + { + StreamString &output_stream = result.GetOutputStream(); + output_stream.Printf ("Breakpoint created: "); + bp->GetDescription(&output_stream, lldb::eDescriptionLevelBrief); + output_stream.EOL(); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Breakpoint creation failed: No breakpoint created in module '%s'.\n", + m_options.m_modules[i].c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + bp = target->CreateBreakpoint (NULL, + file, + m_options.m_line_num, + m_options.m_ignore_inlines).get(); + } + break; + case eSetTypeAddress: // Breakpoint by address + bp = target->CreateBreakpoint (m_options.m_load_addr, false).get(); + break; + case eSetTypeFunctionName: // Breakpoint by function name + if (use_module) + { + for (int i = 0; i < num_modules; ++i) + { + module.SetFile(m_options.m_modules[i].c_str()); + bp = target->CreateBreakpoint (&module, m_options.m_func_name.c_str()).get(); + if (bp) + { + StreamString &output_stream = result.GetOutputStream(); + output_stream.Printf ("Breakpoint created: "); + bp->GetDescription(&output_stream, lldb::eDescriptionLevelBrief); + output_stream.EOL(); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Breakpoint creation failed: No breakpoint created in module '%s'.\n", + m_options.m_modules[i].c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + bp = target->CreateBreakpoint (NULL, m_options.m_func_name.c_str()).get(); + break; + case eSetTypeFunctionRegexp: // Breakpoint by regular expression function name + { + RegularExpression regexp(m_options.m_func_regexp.c_str()); + if (use_module) + { + for (int i = 0; i < num_modules; ++i) + { + module.SetFile(m_options.m_modules[i].c_str()); + bp = target->CreateBreakpoint (&module, regexp).get(); + if (bp) + { + StreamString &output_stream = result.GetOutputStream(); + output_stream.Printf ("Breakpoint created: "); + bp->GetDescription(&output_stream, lldb::eDescriptionLevelBrief); + output_stream.EOL(); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat("Breakpoint creation failed: No breakpoint created in module '%s'.\n", + m_options.m_modules[i].c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + bp = target->CreateBreakpoint (NULL, regexp).get(); + } + break; + default: + break; + } + + if (bp && !use_module) + { + StreamString &output_stream = result.GetOutputStream(); + output_stream.Printf ("Breakpoint created: "); + bp->GetDescription(&output_stream, lldb::eDescriptionLevelBrief); + output_stream.EOL(); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else if (!bp) + { + result.AppendError ("Breakpoint creation failed: No breakpoint created."); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); +} + + + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- + +CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint (CommandInterpreter *interpreter) : + CommandObjectMultiword ("breakpoint", + "A set of commands for operating on breakpoints.", + "breakpoint []") +{ + bool status; + + CommandObjectSP list_command_object (new CommandObjectBreakpointList ()); + CommandObjectSP delete_command_object (new CommandObjectBreakpointDelete ()); + CommandObjectSP enable_command_object (new CommandObjectBreakpointEnable ()); + CommandObjectSP disable_command_object (new CommandObjectBreakpointDisable ()); + CommandObjectSP set_command_object (new CommandObjectBreakpointSet ()); + CommandObjectSP command_command_object (new CommandObjectBreakpointCommand (interpreter)); + + enable_command_object->SetCommandName("breakpoint enable"); + disable_command_object->SetCommandName("breakpoint disable"); + set_command_object->SetCommandName("breakpoint set"); + command_command_object->SetCommandName ("breakpoint command"); + list_command_object->SetCommandName ("breakpoint list"); + + status = LoadSubCommand (list_command_object, "list", interpreter); + status = LoadSubCommand (enable_command_object, "enable", interpreter); + status = LoadSubCommand (disable_command_object, "disable", interpreter); + status = LoadSubCommand (delete_command_object, "delete", interpreter); + status = LoadSubCommand (set_command_object, "set", interpreter); + status = LoadSubCommand (command_command_object, "command", interpreter); +} + +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. + + Args temp_args; + + // 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 ((const char **) temp_args.GetArgumentVector(), 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. + + for (int i = 0; i < valid_ids->Size(); ++i) + { + BreakpointID cur_bp_id = valid_ids->GetBreakpointIDAtIndex (i); + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + if (breakpoint != NULL) + { + int 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->Size() + 1; + result.AppendErrorWithFormat ("'%s' is not a currently valid breakpoint/location id.\n", + id_str.GetData()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + i = valid_ids->Size() + 1; + result.AppendErrorWithFormat ("'%d' is not a currently valid breakpoint id.\n", cur_bp_id.GetBreakpointID()); + result.SetStatus (eReturnStatusFailed); + } + } + } +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointList::Options +//------------------------------------------------------------------------- + +CommandObjectBreakpointList::CommandOptions::CommandOptions() : + Options (), + m_level (lldb::eDescriptionLevelFull) // Breakpoint List defaults to brief descriptions +{ + BuildValidOptionSets(); +} + +CommandObjectBreakpointList::CommandOptions::~CommandOptions () +{ +} + +lldb::OptionDefinition +CommandObjectBreakpointList::CommandOptions::g_option_table[] = +{ + { 0, false, "brief", 'b', no_argument, NULL, 0, NULL, + "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. + { 0, false, "internal", 'i', no_argument, NULL, 0, NULL, + "Show debugger internal breakpoints" }, + + { 1, false, "full", 'f', no_argument, NULL, 0, NULL, + "Give a full description of the breakpoint and its locations."}, + // DITTO FIXME + { 1, false, "internal", 'i', no_argument, NULL, 0, NULL, + "Show debugger internal breakpoints" }, + + { 2, false, "verbose", 'v', no_argument, NULL, 0, NULL, + "Explain everything we know about the breakpoint (for debugging debugger bugs)." }, + // DITTO FIXME + { 2, false, "internal", 'i', no_argument, NULL, 0, NULL, + "Show debugger internal breakpoints" }, + + { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + +const lldb::OptionDefinition* +CommandObjectBreakpointList::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +Error +CommandObjectBreakpointList::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + char short_option = (char) 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'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectBreakpointList::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + + m_level = lldb::eDescriptionLevelFull; + m_internal = false; +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointList +//------------------------------------------------------------------------- + +CommandObjectBreakpointList::CommandObjectBreakpointList () : + CommandObject ("breakpoint list", + "List some or all breakpoints at configurable levels of detail.", + "breakpoint list []") +{ +} + +CommandObjectBreakpointList::~CommandObjectBreakpointList () +{ +} + +Options * +CommandObjectBreakpointList::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectBreakpointList::Execute +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("Invalid target, set executable file using 'file' command."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + const BreakpointList &breakpoints = target->GetBreakpointList(m_options.m_internal); + size_t num_breakpoints = breakpoints.GetSize(); + + if (num_breakpoints == 0) + { + result.AppendMessage ("No breakpoints currently set."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + StreamString &output_stream = result.GetOutputStream(); + + if (args.GetArgumentCount() == 0) + { + // No breakpoint selected; show info about all currently set breakpoints. + result.AppendMessage ("Current breakpoints:"); + for (int i = 0; i < num_breakpoints; ++i) + { + Breakpoint *breakpoint = breakpoints.GetBreakpointByIndex (i).get(); + AddBreakpointDescription (context, &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 (args, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + for (int i = 0; i < valid_bp_ids.Size(); ++i) + { + BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i); + Breakpoint *breakpoint = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get(); + AddBreakpointDescription (context, &output_stream, breakpoint, m_options.m_level); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("Invalid breakpoint id."); + result.SetStatus (eReturnStatusFailed); + } + } + + return result.Succeeded(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointEnable +//------------------------------------------------------------------------- + +CommandObjectBreakpointEnable::CommandObjectBreakpointEnable () : + CommandObject ("enable", + "Enables the specified disabled breakpoint(s). If no breakpoints are specified, enables all of them.", + "breakpoint enable [ | ]") +{ + // This command object can either be called via 'enable' or 'breakpoint enable'. Because it has two different + // potential invocation methods, we need to be a little tricky about generating the syntax string. + //StreamString tmp_string; + //tmp_string.Printf ("%s ", GetCommandName()); + //m_cmd_syntax.assign (tmp_string.GetData(), tmp_string.GetSize()); +} + + +CommandObjectBreakpointEnable::~CommandObjectBreakpointEnable () +{ +} + + +bool +CommandObjectBreakpointEnable::Execute (Args& args, CommandContext *context, + CommandInterpreter *interpreter, CommandReturnObject &result) +{ + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("Invalid target, set executable file using 'file' command."); + 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 be enabled."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (args.GetArgumentCount() == 0) + { + // No breakpoint selected; enable all currently set breakpoints. + target->EnableAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints enabled. (%d breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; enable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (args, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + int enable_count = 0; + int loc_count = 0; + for (int i = 0; i < valid_bp_ids.Size(); ++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); + breakpoint->SetEnabled (true); + ++loc_count; + } + } + else + { + target->EnableBreakpointByID (cur_bp_id.GetBreakpointID()); + ++enable_count; + + int num_locations = breakpoint->GetNumLocations (); + for (int j = 0; j < num_locations; ++j) + { + BreakpointLocation *cur_loc = breakpoint->GetLocationAtIndex(j).get(); + if (cur_loc) + cur_loc->SetEnabled (true); + } + } + } + } + result.AppendMessageWithFormat ("%d breakpoints enabled.\n", enable_count + loc_count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + + return result.Succeeded(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDisable +//------------------------------------------------------------------------- + +CommandObjectBreakpointDisable::CommandObjectBreakpointDisable () : + CommandObject ("disable", + "Disables the specified breakpoint(s) without removing it/them. If no breakpoints are specified, disables them all.", + "disable [ | ]") +{ + // This command object can either be called via 'enable' or 'breakpoint enable'. Because it has two different + // potential invocation methods, we need to be a little tricky about generating the syntax string. + //StreamString tmp_string; + //tmp_string.Printf ("%s ", GetCommandName()); + //m_cmd_syntax.assign(tmp_string.GetData(), tmp_string.GetSize()); +} + +CommandObjectBreakpointDisable::~CommandObjectBreakpointDisable () +{ +} + +bool +CommandObjectBreakpointDisable::Execute (Args& args, CommandContext *context, + CommandInterpreter *interpreter, CommandReturnObject &result) +{ + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("Invalid target, set executable file using 'file' command."); + 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 be disabled."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (args.GetArgumentCount() == 0) + { + // No breakpoint selected; disable all currently set breakpoints. + target->DisableAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints disabled. (%d breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (args, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + int disable_count = 0; + int loc_count = 0; + for (int i = 0; i < valid_bp_ids.Size(); ++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 + { + target->DisableBreakpointByID (cur_bp_id.GetBreakpointID()); + ++disable_count; + + int num_locations = breakpoint->GetNumLocations(); + for (int j = 0; j < num_locations; ++j) + { + BreakpointLocation *cur_loc = breakpoint->GetLocationAtIndex(j).get(); + if (cur_loc) + cur_loc->SetEnabled (false); + } + } + } + } + result.AppendMessageWithFormat ("%d breakpoints disabled.\n", disable_count + loc_count); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + + return result.Succeeded(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDelete +//------------------------------------------------------------------------- + +CommandObjectBreakpointDelete::CommandObjectBreakpointDelete() : + CommandObject ("breakpoint delete", + "Delete the specified breakpoint(s). If no breakpoints are specified, deletes them all.", + "breakpoint delete [ | ]") +{ +} + + +CommandObjectBreakpointDelete::~CommandObjectBreakpointDelete () +{ +} + +bool +CommandObjectBreakpointDelete::Execute (Args& args, CommandContext *context, + CommandInterpreter *interpreter, CommandReturnObject &result) +{ + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("Invalid target, set executable file using 'file' command."); + 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 be deleted."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (args.GetArgumentCount() == 0) + { + // No breakpoint selected; disable all currently set breakpoints. + if (args.GetArgumentCount() != 0) + { + result.AppendErrorWithFormat ("Specify breakpoints to delete with the -i option.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + target->RemoveAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints removed. (%d breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (args, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + int delete_count = 0; + int disable_count = 0; + for (int i = 0; i < valid_bp_ids.Size(); ++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(); +} diff --git a/lldb/source/Commands/CommandObjectBreakpoint.h b/lldb/source/Commands/CommandObjectBreakpoint.h new file mode 100644 index 000000000000..49007438b287 --- /dev/null +++ b/lldb/source/Commands/CommandObjectBreakpoint.h @@ -0,0 +1,235 @@ +//===-- 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/Core/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); + +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpointSet +//------------------------------------------------------------------------- + + +class CommandObjectBreakpointSet : public CommandObject +{ +public: + + typedef enum BreakpointSetType + { + eSetTypeInvalid, + eSetTypeFileAndLine, + eSetTypeAddress, + eSetTypeFunctionName, + eSetTypeFunctionRegexp, + } BreakpointSetType; + + CommandObjectBreakpointSet (); + + virtual + ~CommandObjectBreakpointSet (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual Options * + GetOptions (); + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_filename; + unsigned int m_line_num; + unsigned int m_column; + bool m_ignore_inlines; + std::string m_func_name; + std::string m_func_regexp; + lldb::addr_t m_load_addr; + STLStringArray m_modules; + + }; + +private: + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointEnable +//------------------------------------------------------------------------- + +class CommandObjectBreakpointEnable : public CommandObject +{ +public: + CommandObjectBreakpointEnable (); + + virtual + ~CommandObjectBreakpointEnable (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +private: +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDisable +//------------------------------------------------------------------------- + +class CommandObjectBreakpointDisable : public CommandObject +{ +public: + CommandObjectBreakpointDisable (); + + virtual + ~CommandObjectBreakpointDisable (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +private: +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointList +//------------------------------------------------------------------------- + +class CommandObjectBreakpointList : public CommandObject +{ +public: + CommandObjectBreakpointList (); + + virtual + ~CommandObjectBreakpointList (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual Options * + GetOptions (); + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition * + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + lldb::DescriptionLevel m_level; + + bool m_internal; + }; + +private: + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDelete +//------------------------------------------------------------------------- + +class CommandObjectBreakpointDelete : public CommandObject +{ +public: + CommandObjectBreakpointDelete (); + + virtual + ~CommandObjectBreakpointDelete (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +private: +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectBreakpoint_h_ diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp new file mode 100644 index 000000000000..8a5a443ea8e4 --- /dev/null +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -0,0 +1,695 @@ +//===-- CommandObjectBreakpointCommand.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 + + +#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::CommandOptions +//------------------------------------------------------------------------- + +CommandObjectBreakpointCommandAdd::CommandOptions::CommandOptions () : + Options () +{ + BuildValidOptionSets(); +} + +CommandObjectBreakpointCommandAdd::CommandOptions::~CommandOptions () +{ +} + +lldb::OptionDefinition +CommandObjectBreakpointCommandAdd::CommandOptions::g_option_table[] = +{ + { 0, true, "script", 's', no_argument, NULL, 0, NULL, + "Write the breakpoint command script in the default scripting language."}, + + { 1, true, "python", 'p', no_argument, NULL, 0, NULL, + "Write the breakpoint command script in the Python scripting language."}, + + { 2, true, "commands", 'c', no_argument, NULL, 0, NULL, + "Write the breakpoint command script using the command line commands."}, + + { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + +const lldb::OptionDefinition* +CommandObjectBreakpointCommandAdd::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + + +Error +CommandObjectBreakpointCommandAdd::CommandOptions::SetOptionValue +( + int option_idx, + const char *option_arg +) +{ + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': + m_use_commands = false; + m_use_script_language = true; + m_script_language = eScriptLanguageDefault; + break; + case 'p': + m_use_commands = false; + m_use_script_language = true; + m_script_language = eScriptLanguagePython; + break; + case 'c': + m_use_commands = true; + m_use_script_language = false; + m_script_language = eScriptLanguageNone; + break; + default: + break; + } + return error; +} + +void +CommandObjectBreakpointCommandAdd::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + + m_use_commands = false; + m_use_script_language = false; + m_script_language = eScriptLanguageNone; +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandAdd +//------------------------------------------------------------------------- + + +CommandObjectBreakpointCommandAdd::CommandObjectBreakpointCommandAdd () : + CommandObject ("add", + "Adds a set of commands to a breakpoint to be executed whenever a breakpoint is hit.", + "breakpoint command add ") +{ + SetHelpLong ( +"\nGeneral information about entering breakpoint 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 breakpoint 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 breakpoint 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 breakpoint \n\ +and executed when the breakpoint is hit. \n\ + \n\ +Syntax checking is not necessarily done when breakpoint commands are \n\ +entered. An improperly written breakpoint command will attempt to get \n\ +executed when the breakpoint gets hit, and usually silently fail. If \n\ +your breakpoint command does not appear to be getting executed, go \n\ +back and check your syntax. \n\ + \n\ + \n\ +Special information about PYTHON breakpoint 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 breakpoint gets hit. If you enter function \n\ +definitions, they will be passed to the Python interpreter as soon as \n\ +you finish entering the breakpoint command, and they can be called \n\ +later (don't forget to add calls to them, if you want them called when \n\ +the breakpoint 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 breakpoint. Important \n\ +Note: Because loose Python code gets collected into functions, if you \n\ +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\ +breakpoint commands. \n\ + \n\ +Example Python one-line breakpoint command: \n\ + \n\ +(lldb) breakpoint command add -p 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> print \"Hit this breakpoint!\" \n\ +> DONE \n\ + \n\ +Example multiple line Python breakpoint command, using function definition: \n\ + \n\ +(lldb) breakpoint command add -p 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 -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\ +Special information debugger command breakpoint 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" ); +} + +CommandObjectBreakpointCommandAdd::~CommandObjectBreakpointCommandAdd () +{ +} + +bool +CommandObjectBreakpointCommandAdd::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + Target *target = context->GetTarget(); + + 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 (command.GetArgumentCount() == 0) + { + result.AppendError ("No breakpoint specified to which to add the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + for (int i = 0; i < valid_bp_ids.Size(); ++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) + { + if (m_options.m_use_script_language) + { + interpreter->GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp_loc_sp->GetLocationOptions(), + result); + } + else + { + CollectDataForBreakpointCommandCallback (bp_loc_sp->GetLocationOptions(), result); + } + } + } + else + { + if (m_options.m_use_script_language) + { + interpreter->GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp->GetOptions(), + result); + } + else + { + CollectDataForBreakpointCommandCallback (bp->GetOptions(), result); + } + } + } + } + } + + return result.Succeeded(); +} + +Options * +CommandObjectBreakpointCommandAdd::GetOptions () +{ + return &m_options; +} + +const char *g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; + +void +CommandObjectBreakpointCommandAdd::CollectDataForBreakpointCommandCallback +( + BreakpointOptions *bp_options, + CommandReturnObject &result +) +{ + InputReaderSP reader_sp (new InputReader()); + std::auto_ptr data_ap(new BreakpointOptions::CommandData()); + if (reader_sp && data_ap.get()) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (CommandObjectBreakpointCommand::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()) + { + Debugger::GetSharedInstance().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + +} + +size_t +CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + + switch (notification) + { + case eInputReaderActivate: + if (out_fh) + { + ::fprintf (out_fh, "%s\n", g_reader_instructions); + if (reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (out_fh && reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + 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 (out_fh && !reader->IsDone() && reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; +} + + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandRemove +//------------------------------------------------------------------------- + +CommandObjectBreakpointCommandRemove::CommandObjectBreakpointCommandRemove () : + CommandObject ("remove", + "Remove the set of commands from a breakpoint.", + "breakpoint command remove ") +{ +} + +CommandObjectBreakpointCommandRemove::~CommandObjectBreakpointCommandRemove () +{ +} + +bool +CommandObjectBreakpointCommandRemove::Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + Target *target = context->GetTarget(); + + if (target == NULL) + { + result.AppendError ("There is not a current executable; there are no breakpoints from which to remove 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 removed"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + result.AppendError ("No breakpoint specified from which to remove the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + for (int i = 0; i < valid_bp_ids.Size(); ++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 +//------------------------------------------------------------------------- + +CommandObjectBreakpointCommandList::CommandObjectBreakpointCommandList () : + CommandObject ("List", + "List the script or set of commands to be executed when the breakpoint is hit.", + "breakpoint command list ") +{ +} + +CommandObjectBreakpointCommandList::~CommandObjectBreakpointCommandList () +{ +} + +bool +CommandObjectBreakpointCommandList::Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + Target *target = context->GetTarget(); + + 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()) + { + for (int i = 0; i < valid_bp_ids.Size(); ++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) + { + 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->GetOptionsNoCopy(); + 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()); + 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 ("command", + "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').", + "command [] ") +{ + bool status; + CommandObjectSP add_command_object (new CommandObjectBreakpointCommandAdd ()); + CommandObjectSP remove_command_object (new CommandObjectBreakpointCommandRemove ()); + CommandObjectSP list_command_object (new CommandObjectBreakpointCommandList ()); + + add_command_object->SetCommandName ("breakpoint command add"); + remove_command_object->SetCommandName ("breakpoint command remove"); + list_command_object->SetCommandName ("breakpoint command list"); + + status = LoadSubCommand (add_command_object, "add", interpreter); + status = LoadSubCommand (remove_command_object, "remove", interpreter); + status = LoadSubCommand (list_command_object, "list", interpreter); +} + + +CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand () +{ +} + +bool +CommandObjectBreakpointCommand::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) + { + uint32_t num_commands = commands.GetSize(); + CommandInterpreter &interpreter = Debugger::GetSharedInstance().GetCommandInterpreter(); + CommandReturnObject result; + ExecutionContext exe_ctx = context->context; + + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + FILE *err_fh = Debugger::GetSharedInstance().GetErrorFileHandle(); + + + uint32_t i; + for (i = 0; i < num_commands; ++i) + { + + // First time through we use the context from the stoppoint, after that we use whatever + // has been set by the previous command. + + if (!interpreter.HandleCommand (commands.GetStringAtIndex(i), false, result, &exe_ctx)) + break; + + // FIXME: This isn't really the right way to do this. We should be able to peek at the public + // to see if there is any new events, but that is racey, since the internal process thread has to run and + // deliver the event to the public queue before a run will show up. So for now we check + // the internal thread state. + + lldb::StateType internal_state = exe_ctx.process->GetPrivateState(); + if (internal_state != eStateStopped) + { + if (i < num_commands - 1) + { + if (out_fh) + ::fprintf (out_fh, "Short-circuiting command execution because target state changed to %s." + " last command: \"%s\"\n", StateAsCString(internal_state), + commands.GetStringAtIndex(i)); + } + break; + } + + // First time through we use the context from the stoppoint, after that we use whatever + // has been set by the previous command. + exe_ctx = Debugger::GetSharedInstance().GetCurrentExecutionContext(); + + + if (out_fh) + ::fprintf (out_fh, "%s", result.GetErrorStream().GetData()); + if (err_fh) + ::fprintf (err_fh, "%s", result.GetOutputStream().GetData()); + result.Clear(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + + if (err_fh && !result.Succeeded() && i < num_commands) + ::fprintf (err_fh, "Attempt to execute '%s' failed.\n", commands.GetStringAtIndex(i)); + + if (out_fh) + ::fprintf (out_fh, "%s", result.GetErrorStream().GetData()); + + if (err_fh) + ::fprintf (err_fh, "%s", result.GetOutputStream().GetData()); + } + return ret_value; +} + diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.h b/lldb/source/Commands/CommandObjectBreakpointCommand.h new file mode 100644 index 000000000000..2ba2c6075e88 --- /dev/null +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.h @@ -0,0 +1,169 @@ +//===-- 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/Core/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 (); + + + static bool + BreakpointOptionsCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandAdd +//------------------------------------------------------------------------- + + +class CommandObjectBreakpointCommandAdd : public CommandObject +{ +public: + + CommandObjectBreakpointCommandAdd (); + + virtual + ~CommandObjectBreakpointCommandAdd (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual Options * + GetOptions (); + + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + static size_t + GenerateBreakpointCommandCallback (void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static bool + BreakpointOptionsCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::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; + }; + +private: + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandRemove +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommandRemove : public CommandObject +{ +public: + CommandObjectBreakpointCommandRemove (); + + virtual + ~CommandObjectBreakpointCommandRemove (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +private: +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandList +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommandList : public CommandObject +{ +public: + CommandObjectBreakpointCommandList (); + + virtual + ~CommandObjectBreakpointCommandList (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +private: +}; + + +} // namespace lldb_private + +#endif // liblldb_CommandObjectBreakpointCommand_h_ diff --git a/lldb/source/Commands/CommandObjectCall.cpp b/lldb/source/Commands/CommandObjectCall.cpp new file mode 100644 index 000000000000..58d0a0e9f110 --- /dev/null +++ b/lldb/source/Commands/CommandObjectCall.cpp @@ -0,0 +1,307 @@ +//===-- CommandObjectCall.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectCall.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.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/CommandContext.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/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. +// + +CommandObjectCall::CommandOptions::CommandOptions () : + Options() +{ + // Keep only one place to reset the values to their defaults + ResetOptionValues(); +} + + +CommandObjectCall::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectCall::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'l': + if (language.SetLanguageFromCString (option_arg) == false) + { + error.SetErrorStringWithFormat("Invalid language option argument '%s'.\n", option_arg); + } + break; + + case 'g': + debug = true; + break; + + case 'f': + error = Args::StringToFormat(option_arg,format); + break; + + case 'n': + noexecute = true; + break; + + case 'a': + use_abi = true; + break; + + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectCall::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + language.Clear(); + debug = false; + format = eFormatDefault; + show_types = true; + show_summary = true; + noexecute = false; + use_abi = false; +} + +const lldb::OptionDefinition* +CommandObjectCall::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectCall::CommandObjectCall () : + CommandObject ( + "call", + "Call a function.", + "call [[ ] ... ] []", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) +{ +} + +CommandObjectCall::~CommandObjectCall () +{ +} + +Options * +CommandObjectCall::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectCall::Execute +( + Args &command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + ConstString target_triple; + int num_args = command.GetArgumentCount(); + + Target *target = context->GetTarget (); + if (target) + target->GetTargetTriple(target_triple); + + if (!target_triple) + target_triple = Host::GetTargetTriple (); + + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.thread == NULL || exe_ctx.frame == NULL) + { + result.AppendError ("No currently selected thread and frame."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (num_args < 2) + { + result.AppendErrorWithFormat ("Invalid usage, should be: %s.\n", GetSyntax()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if ((num_args - 2) %2 != 0) + { + result.AppendErrorWithFormat ("Invalid usage - unmatched args & types, should be: %s.\n", GetSyntax()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (target_triple) + { + //const char *return_type = command.GetArgumentAtIndex(0); + const char *function_name = command.GetArgumentAtIndex(1); + // Look up the called function: + + Function *target_fn = exe_ctx.frame->GetSymbolContext(eSymbolContextEverything).FindFunctionByName (function_name); + + // FIXME: If target_fn is NULL, we should look up the name as a symbol and use it and the provided + // return type. + + if (target_fn == NULL) + { + result.AppendErrorWithFormat ("Could not find function '%s'.\n", function_name); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ValueList value_list; + // Okay, now parse arguments. For now we only accept basic types. + for (int i = 2; i < num_args; i+= 2) + { + const char *type_str = command.GetArgumentAtIndex(i); + const char *value_str = command.GetArgumentAtIndex(i + 1); + bool success; + if (strcmp(type_str, "int") == 0 + || strcmp(type_str, "int32_t") == 0) + { + value_list.PushValue(Value(Args::StringToSInt32(value_str, 0, 0, &success))); + } + else if (strcmp (type_str, "int64_t") == 0) + { + value_list.PushValue(Value(Args::StringToSInt64(value_str, 0, 0, &success))); + } + else if (strcmp(type_str, "uint") == 0 + || strcmp(type_str, "uint32_t") == 0) + { + value_list.PushValue(Value(Args::StringToUInt32(value_str, 0, 0, &success))); + } + else if (strcmp (type_str, "uint64_t") == 0) + { + value_list.PushValue(Value(Args::StringToUInt64(value_str, 0, 0, &success))); + } + else if (strcmp (type_str, "cstr") == 0) + { + Value val ((intptr_t)value_str); + val.SetValueType (Value::eValueTypeHostAddress); + + + void *cstr_type = target->GetScratchClangASTContext()->GetCStringType(true); + val.SetContext (Value::eContextTypeOpaqueClangQualType, cstr_type); + value_list.PushValue(val); + + success = true; + } + + if (!success) + { + result.AppendErrorWithFormat ("Could not convert value: '%s' to type '%s'.\n", value_str, type_str); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + // Okay, we have the function and the argument list and the return type. Now make a ClangFunction object and + // run it: + + StreamString errors; + ClangFunction clang_fun (target_triple.GetCString(), *target_fn, target->GetScratchClangASTContext(), value_list); + if (m_options.noexecute) + { + // Now write down the argument values for this call. + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + if (!clang_fun.InsertFunction (exe_ctx, args_addr, errors)) + { + result.AppendErrorWithFormat("Error inserting function: '%s'.\n", errors.GetData()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + result.Succeeded(); + return true; + } + } + + ClangFunction::ExecutionResults return_status; + Value return_value; + + if (m_options.use_abi) + { + return_status = clang_fun.ExecuteFunctionWithABI(exe_ctx, errors, return_value); + } + else + { + bool stop_others = true; + return_status = clang_fun.ExecuteFunction(exe_ctx, errors, stop_others, NULL, return_value); + } + + // Now figure out what to do with the return value. + if (return_status == ClangFunction::eExecutionSetupError) + { + result.AppendErrorWithFormat("Error setting up function execution: '%s'.\n", errors.GetData()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (return_status != ClangFunction::eExecutionCompleted) + { + result.AppendWarningWithFormat("Interrupted while calling function: '%s'.\n", errors.GetData()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + else + { + // Now print out the result. + result.GetOutputStream().Printf("Return value: "); + return_value.Dump(&(result.GetOutputStream())); + result.Succeeded(); + } + + } + else + { + result.AppendError ("invalid target triple"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); +} + +lldb::OptionDefinition +CommandObjectCall::CommandOptions::g_option_table[] = +{ +{ 0, true, "language", 'l', required_argument, NULL, 0, "[c|c++|objc|objc++]", "Sets the language to use when parsing the expression."}, +{ 0, false, "format", 'f', required_argument, NULL, 0, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]", "Specify the format that the expression output should use."}, +{ 0, false, "debug", 'g', no_argument, NULL, 0, NULL, "Enable verbose debug logging of the expression parsing and evaluation."}, +{ 0, false, "noexecute", 'n', no_argument, NULL, 0, "no execute", "Only JIT and copy the wrapper & arguments, but don't execute."}, +{ 0, false, "useabi", 'a', no_argument, NULL, 0, NULL, "Use the ABI instead of the JIT to marshall arguments."}, +{ 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + diff --git a/lldb/source/Commands/CommandObjectCall.h b/lldb/source/Commands/CommandObjectCall.h new file mode 100644 index 000000000000..c051a142ece7 --- /dev/null +++ b/lldb/source/Commands/CommandObjectCall.h @@ -0,0 +1,84 @@ +//===-- CommandObjectCall.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectCall_h_ +#define liblldb_CommandObjectCall_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Core/Options.h" +#include "lldb/Core/Language.h" + +namespace lldb_private { + +class CommandObjectCall : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + Language language; + lldb::Encoding encoding; + lldb::Format format; + bool debug; + bool show_types; + bool show_summary; + bool noexecute; + bool use_abi; + }; + + CommandObjectCall (); + + virtual + ~CommandObjectCall (); + + virtual + Options * + GetOptions (); + + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + WantsRawCommandString() { return false; } + +protected: + + CommandOptions m_options; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectCall_h_ diff --git a/lldb/source/Commands/CommandObjectDelete.cpp b/lldb/source/Commands/CommandObjectDelete.cpp new file mode 100644 index 000000000000..2fc072e2de4e --- /dev/null +++ b/lldb/source/Commands/CommandObjectDelete.cpp @@ -0,0 +1,32 @@ +//===-- CommandObjectDelete.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectDelete.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectDelete +//------------------------------------------------------------------------- + +CommandObjectDelete::CommandObjectDelete () : +CommandObjectCrossref ("delete", "Lists the kinds of objects you can delete, and shows syntax for deleting them.", "delete") +{ +} + +CommandObjectDelete::~CommandObjectDelete () +{ +} + + diff --git a/lldb/source/Commands/CommandObjectDelete.h b/lldb/source/Commands/CommandObjectDelete.h new file mode 100644 index 000000000000..f7d86b7aef77 --- /dev/null +++ b/lldb/source/Commands/CommandObjectDelete.h @@ -0,0 +1,37 @@ +//===-- CommandObjectDelete.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectDelete_h_ +#define liblldb_CommandObjectDelete_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectCrossref.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectDelete +//------------------------------------------------------------------------- + +class CommandObjectDelete : public CommandObjectCrossref +{ +public: + CommandObjectDelete (); + + virtual + ~CommandObjectDelete (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectDelete_h_ diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp new file mode 100644 index 000000000000..0985504e4ffc --- /dev/null +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -0,0 +1,431 @@ +//===-- 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 "CommandObjectDisassemble.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Options.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#define DEFAULT_DISASM_BYTE_SIZE 32 + +using namespace lldb; +using namespace lldb_private; + +CommandObjectDisassemble::CommandOptions::CommandOptions () : + Options(), + m_func_name(), + m_load_addr() +{ + ResetOptionValues(); +} + +CommandObjectDisassemble::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectDisassemble::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'm': + show_mixed = true; + break; + + case 'c': + num_lines_context = Args::StringToUInt32(option_arg, 0, 0); + break; + + case 'b': + show_bytes = true; + break; + + case 'a': + m_load_addr = Args::StringToUInt64(optarg, LLDB_INVALID_ADDRESS, 0); + if (m_load_addr == LLDB_INVALID_ADDRESS) + m_load_addr = Args::StringToUInt64(optarg, LLDB_INVALID_ADDRESS, 16); + + if (m_load_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("Invalid address string '%s'.\n", optarg); + break; + + case 'n': + m_func_name = option_arg; + break; + + case 'r': + raw = true; + break; + + default: + error.SetErrorStringWithFormat("Unrecognized short option '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectDisassemble::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + show_mixed = false; + show_bytes = false; + num_lines_context = 0; + m_func_name.clear(); + m_load_addr = LLDB_INVALID_ADDRESS; +} + +const lldb::OptionDefinition* +CommandObjectDisassemble::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +lldb::OptionDefinition +CommandObjectDisassemble::CommandOptions::g_option_table[] = +{ +{ 0, false, "bytes", 'b', no_argument, NULL, 0, NULL, "Show opcode bytes when disassembling."}, +{ 0, false, "context", 'c', required_argument, NULL, 0, "", "Number of context lines of source to show."}, +{ 0, false, "mixed", 'm', no_argument, NULL, 0, NULL, "Enable mixed source and assembly display."}, +{ 0, false, "raw", 'r', no_argument, NULL, 0, NULL, "Print raw disassembly with no symbol information."}, + +{ 1, false, "address", 'a', required_argument, NULL, 0, "
", "Address to start disassembling."}, +{ 1, false, "bytes", 'b', no_argument, NULL, 0, NULL, "Show opcode bytes when disassembling."}, +{ 1, false, "context", 'c', required_argument, NULL, 0, "", "Number of context lines of source to show."}, +{ 1, false, "mixed", 'm', no_argument, NULL, 0, NULL, "Enable mixed source and assembly display."}, +{ 1, false, "raw", 'r', no_argument, NULL, 0, NULL, "Print raw disassembly with no symbol information."}, + +{ 2, false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, "", "Disassemble entire contents of the given function name."}, +{ 2, false, "bytes", 'b', no_argument, NULL, 0, NULL, "Show opcode bytes when disassembling."}, +{ 2, false, "context", 'c', required_argument, NULL, 0, "", "Number of context lines of source to show."}, +{ 2, false, "mixed", 'm', no_argument, NULL, 0, NULL, "Enable mixed source and assembly display."}, +{ 2, false, "raw", 'r', no_argument, NULL, 0, NULL, "Print raw disassembly with no symbol information."}, + +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + + +//------------------------------------------------------------------------- +// CommandObjectDisassemble +//------------------------------------------------------------------------- + +CommandObjectDisassemble::CommandObjectDisassemble () : + CommandObject ("disassemble", + "Disassemble bytes in the current function or anywhere in the inferior program.", + "disassemble [[ []] | ] []") +{ +} + +CommandObjectDisassemble::~CommandObjectDisassemble() +{ +} + +void +CommandObjectDisassemble::Disassemble +( + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result, + Disassembler *disassembler, + const SymbolContextList &sc_list +) +{ + const size_t count = sc_list.GetSize(); + SymbolContext sc; + AddressRange range; + for (size_t i=0; iGetExecutionContext().process); + if (addr != LLDB_INVALID_ADDRESS) + { + lldb::addr_t end_addr = addr + range.GetByteSize(); + Disassemble (context, interpreter, result, disassembler, addr, end_addr); + } + } + } +} + +void +CommandObjectDisassemble::Disassemble +( + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result, + Disassembler *disassembler, + lldb::addr_t addr, + lldb::addr_t end_addr +) +{ + if (addr == LLDB_INVALID_ADDRESS) + return; + + if (end_addr == LLDB_INVALID_ADDRESS || addr >= end_addr) + end_addr = addr + DEFAULT_DISASM_BYTE_SIZE; + + ExecutionContext exe_ctx (context->GetExecutionContext()); + DataExtractor data; + size_t bytes_disassembled = disassembler->ParseInstructions (&exe_ctx, eAddressTypeLoad, addr, end_addr - addr, data); + if (bytes_disassembled == 0) + { + // Nothing got disassembled... + } + else + { + // We got some things disassembled... + size_t num_instructions = disassembler->GetInstructionList().GetSize(); + uint32_t offset = 0; + Stream &output_stream = result.GetOutputStream(); + SymbolContext sc; + SymbolContext prev_sc; + AddressRange sc_range; + if (m_options.show_mixed) + output_stream.IndentMore (); + + for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i); + if (inst) + { + lldb::addr_t curr_addr = addr + offset; + if (m_options.show_mixed) + { + Process *process = context->GetExecutionContext().process; + if (!sc_range.ContainsLoadAddress (curr_addr, process)) + { + prev_sc = sc; + Address curr_so_addr; + if (process && process->ResolveLoadAddress (curr_addr, curr_so_addr)) + { + if (curr_so_addr.GetSection()) + { + Module *module = curr_so_addr.GetSection()->GetModule(); + uint32_t resolved_mask = module->ResolveSymbolContextForAddress(curr_so_addr, eSymbolContextEverything, sc); + if (resolved_mask) + { + sc.GetAddressRange (eSymbolContextEverything, sc_range); + if (sc != prev_sc) + { + if (offset != 0) + output_stream.EOL(); + + sc.DumpStopContext(&output_stream, process, curr_so_addr); + output_stream.EOL(); + if (sc.comp_unit && sc.line_entry.IsValid()) + { + interpreter->GetSourceManager().DisplaySourceLinesWithLineNumbers ( + sc.line_entry.file, + sc.line_entry.line, + m_options.num_lines_context, + m_options.num_lines_context, + m_options.num_lines_context ? "->" : "", + &output_stream); + } + } + } + } + } + } + } + if (m_options.show_mixed) + output_stream.IndentMore (); + output_stream.Indent(); + size_t inst_byte_size = inst->GetByteSize(); + inst->Dump(&output_stream, curr_addr, m_options.show_bytes ? &data : NULL, offset, exe_ctx, m_options.raw); + output_stream.EOL(); + offset += inst_byte_size; + if (m_options.show_mixed) + output_stream.IndentLess (); + } + else + { + break; + } + } + if (m_options.show_mixed) + output_stream.IndentLess (); + + } +} + +bool +CommandObjectDisassemble::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ArchSpec arch(target->GetArchitecture()); + if (!arch.IsValid()) + { + result.AppendError ("target needs valid architecure in order to be able to disassemble"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Disassembler *disassembler = Disassembler::FindPlugin(arch); + + if (disassembler == NULL) + { + result.AppendErrorWithFormat ("Unable to find Disassembler plug-in for %s architecture.\n", arch.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + result.SetStatus (eReturnStatusSuccessFinishResult); + + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + lldb::addr_t end_addr = LLDB_INVALID_ADDRESS; + ConstString name; + const size_t argc = command.GetArgumentCount(); + if (argc == 0 && m_options.m_load_addr != LLDB_INVALID_ADDRESS) + { + addr = m_options.m_load_addr; + end_addr = addr + DEFAULT_DISASM_BYTE_SIZE; + } else if (argc == 0 && !m_options.m_func_name.empty()) + { + ConstString tmpname(m_options.m_func_name.c_str()); + name = tmpname; + } else if (argc == 0) + { + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.frame) + { + SymbolContext sc(exe_ctx.frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); + if (sc.function) + { + addr = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + end_addr = addr + sc.function->GetAddressRange().GetByteSize(); + } + else if (sc.symbol && sc.symbol->GetAddressRangePtr()) + { + addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + { + end_addr = addr + sc.symbol->GetAddressRangePtr()->GetByteSize(); + if (addr == end_addr) + end_addr += DEFAULT_DISASM_BYTE_SIZE; + } + } + else + { + addr = exe_ctx.frame->GetPC().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + end_addr = addr + DEFAULT_DISASM_BYTE_SIZE; + } + } + else + { + result.AppendError ("invalid frame"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else if (argc == 1) + { + const char *arg = command.GetArgumentAtIndex(0); + addr = Args::StringToAddress (arg); + if (addr == LLDB_INVALID_ADDRESS) + { + // Lookup function or symbol name? + ConstString tmpname(arg); + name = tmpname; + } + else + { + end_addr = addr + DEFAULT_DISASM_BYTE_SIZE; + } + } + else if (argc >= 1 && argc <= 2) + { + addr = Args::StringToAddress (command.GetArgumentAtIndex(0)); + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendErrorWithFormat ("Unable to parse address '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return false; + } + end_addr = Args::StringToAddress (command.GetArgumentAtIndex(1), addr); + if (end_addr == LLDB_INVALID_ADDRESS) + { + result.AppendErrorWithFormat ("Unable to parse address '%s'.\n", command.GetArgumentAtIndex(1)); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (!name.IsEmpty()) + { + SymbolContextList sc_list; + + if (target->GetImages().FindFunctions(name, sc_list)) + { + Disassemble (context, interpreter, result, disassembler, sc_list); + } + else if (target->GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeCode, sc_list)) + { + Disassemble (context, interpreter, result, disassembler, sc_list); + } + else + { + result.AppendErrorWithFormat ("Unable to find symbol with name '%s'.\n", name.GetCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (addr < end_addr) + { + Disassemble (context, interpreter, result, disassembler, addr, end_addr); + } + + if (addr == LLDB_INVALID_ADDRESS && name.IsEmpty()) + { + result.AppendError ("No recognizable address of function name provided"); + result.SetStatus (eReturnStatusFailed); + return false; + } + { + return result.Succeeded(); + } +} diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h new file mode 100644 index 000000000000..2cf800d2f590 --- /dev/null +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -0,0 +1,95 @@ +//===-- 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/Interpreter/CommandObject.h" +#include "lldb/Core/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectDisassemble +//------------------------------------------------------------------------- + +class CommandObjectDisassemble : public CommandObject +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + bool show_mixed; // Show mixed source/assembly + bool show_bytes; + uint32_t num_lines_context; + bool raw; + std::string m_func_name; + lldb::addr_t m_load_addr; + static lldb::OptionDefinition g_option_table[]; + }; + + CommandObjectDisassemble (); + + virtual + ~CommandObjectDisassemble (); + + virtual + Options * + GetOptions () + { + return &m_options; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +protected: + CommandOptions m_options; + + void + Disassemble (CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result, + Disassembler *disassembler, + lldb::addr_t addr, + lldb::addr_t end_addr); + + void + Disassemble (CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result, + Disassembler *disassembler, + const SymbolContextList &sc_list); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectDisassemble_h_ diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp new file mode 100644 index 000000000000..9afc8c0a1a3d --- /dev/null +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -0,0 +1,554 @@ +//===-- 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 "CommandObjectExpression.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandReturnObject.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 "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +CommandObjectExpression::CommandOptions::CommandOptions () : + Options() +{ + // Keep only one place to reset the values to their defaults + ResetOptionValues(); +} + + +CommandObjectExpression::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectExpression::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'l': + if (language.SetLanguageFromCString (option_arg) == false) + { + error.SetErrorStringWithFormat("Invalid language option argument '%s'.\n", option_arg); + } + break; + + case 'g': + debug = true; + break; + + case 'f': + error = Args::StringToFormat(option_arg, format); + break; + + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectExpression::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + language.Clear(); + debug = false; + format = eFormatDefault; + show_types = true; + show_summary = true; +} + +const lldb::OptionDefinition* +CommandObjectExpression::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectExpression::CommandObjectExpression () : + CommandObject ( + "expression", + "Evaluate a C expression in the current program context, using variables currently in scope.", + "expression [] "), + m_expr_line_count (0), + m_expr_lines () +{ + SetHelpLong( +"Examples: \n\ +\n\ + expr my_struct->a = my_array[3] \n\ + expr -f bin -- (index * 8) + 5 \n\ + expr char c[] = \"foo\"; c[0]\n"); +} + +CommandObjectExpression::~CommandObjectExpression () +{ +} + +Options * +CommandObjectExpression::GetOptions () +{ + return &m_options; +} + + +bool +CommandObjectExpression::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + return false; +} + + +size_t +CommandObjectExpression::MultiLineExpressionCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton; + + switch (notification) + { + case eInputReaderActivate: + if (out_fh) + ::fprintf (out_fh, "%s\n", "Enter expressions, then terminate with an empty line to evaluate:"); + // Fall through + case eInputReaderReactivate: + //if (out_fh) + // ::fprintf (out_fh, "%3u: ", cmd_object_expr->m_expr_line_count); + break; + + case eInputReaderDeactivate: + 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); + //else if (out_fh && !reader->IsDone()) + // ::fprintf (out_fh, "%3u: ", cmd_object_expr->m_expr_line_count); + break; + + case eInputReaderDone: + { + StreamFile out_stream(Debugger::GetSharedInstance().GetOutputFileHandle()); + StreamFile err_stream(Debugger::GetSharedInstance().GetErrorFileHandle()); + bool bare = false; + cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), + bare, + out_stream, + err_stream); + } + break; + } + + return bytes_len; +} + +bool +CommandObjectExpression::EvaluateExpression (const char *expr, bool bare, Stream &output_stream, Stream &error_stream) +{ + bool success = false; + ConstString target_triple; + Target *target = m_exe_ctx.target; + if (target) + target->GetTargetTriple(target_triple); + + if (!target_triple) + target_triple = Host::GetTargetTriple (); + + + if (target_triple) + { + const bool show_types = m_options.show_types; + const bool show_summary = m_options.show_summary; + const bool debug = m_options.debug; + + ClangExpressionDeclMap expr_decl_map(&m_exe_ctx); + ClangExpression clang_expr(target_triple.AsCString(), &expr_decl_map); + + unsigned num_errors = 0; + + if (bare) + num_errors = clang_expr.ParseBareExpression (llvm::StringRef(expr), error_stream); + else + num_errors = clang_expr.ParseExpression (expr, error_stream); + + if (num_errors == 0) + { + StreamString dwarf_opcodes; + dwarf_opcodes.SetByteOrder(eByteOrderHost); + dwarf_opcodes.GetFlags().Set(Stream::eBinary); + ClangExpressionVariableList expr_local_vars; + clang_expr.ConvertExpressionToDWARF (expr_local_vars, dwarf_opcodes); + + success = true; + + DataExtractor dwarf_opcodes_data(dwarf_opcodes.GetData(), dwarf_opcodes.GetSize(), eByteOrderHost, 8); + DWARFExpression expr(dwarf_opcodes_data, 0, dwarf_opcodes_data.GetByteSize(), NULL); + expr.SetExpressionLocalVariableList(&expr_local_vars); + if (debug) + { + output_stream << "Expression parsed ok, dwarf opcodes:"; + output_stream.IndentMore(); + expr.GetDescription(&output_stream, lldb::eDescriptionLevelVerbose); + output_stream.IndentLess(); + output_stream.EOL(); + } + + clang::ASTContext *ast_context = clang_expr.GetASTContext(); + Value expr_result; + Error expr_error; + bool expr_success = expr.Evaluate (&m_exe_ctx, ast_context, NULL, expr_result, &expr_error); + if (expr_success) + { + lldb::Format format = m_options.format; + + // Resolve any values that are possible + expr_result.ResolveValue(&m_exe_ctx, ast_context); + + if (expr_result.GetContextType() == Value::eContextTypeInvalid && + expr_result.GetValueType() == Value::eValueTypeScalar && + format == eFormatDefault) + { + // The expression result is just a scalar with no special formatting + expr_result.GetScalar().GetValue (&output_stream, show_types); + output_stream.EOL(); + } + else + { + DataExtractor data; + expr_error = expr_result.GetValueAsData (&m_exe_ctx, ast_context, data, 0); + if (expr_error.Success()) + { + if (format == eFormatDefault) + format = expr_result.GetValueDefaultFormat (); + + void *clang_type = expr_result.GetValueOpaqueClangQualType(); + if (clang_type) + { + if (show_types) + Type::DumpClangTypeName(&output_stream, clang_type); + + Type::DumpValue ( + &m_exe_ctx, // The execution context for memory and variable access + ast_context, // The ASTContext that the clang type belongs to + clang_type, // The opaque clang type we want to dump that value of + &output_stream, // Stream to dump to + format, // Format to use when dumping + data, // A buffer containing the bytes for the clang type + 0, // Byte offset within "data" where value is + data.GetByteSize(), // Size in bytes of the value we are dumping + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Show types? + show_summary, // Show summary? + debug, // Debug logging output? + UINT32_MAX); // Depth to dump in case this is an aggregate type + } + else + { + data.Dump(&output_stream, // Stream to dump to + 0, // Byte offset within "data" + format, // Format to use when dumping + data.GetByteSize(), // Size in bytes of each item we are dumping + 1, // Number of items to dump + UINT32_MAX, // Number of items per line + LLDB_INVALID_ADDRESS, // Invalid address, don't show any offset/address context + 0, // Bitfield bit size + 0); // Bitfield bit offset + } + output_stream.EOL(); + } + else + { + error_stream.Printf ("error: %s\n", expr_error.AsCString()); + success = false; + } + } + } + else + { + error_stream.Printf ("error: %s\n", expr_error.AsCString()); + } + } + } + else + { + error_stream.PutCString ("error: invalid target triple\n"); + } + + return success; +} + +bool +CommandObjectExpression::ExecuteRawCommandString +( + const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + ConstString target_triple; + Target *target = context->GetTarget (); + if (target) + target->GetTargetTriple(target_triple); + + if (!target_triple) + target_triple = Host::GetTargetTriple (); + + ExecutionContext exe_ctx(context->GetExecutionContext()); + + Stream &output_stream = result.GetOutputStream(); + + m_options.ResetOptionValues(); + + const char * expr = NULL; + + if (command[0] == '\0') + { + m_expr_lines.clear(); + m_expr_line_count = 0; + + InputReaderSP reader_sp (new InputReader()); + 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()) + { + Debugger::GetSharedInstance().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, interpreter, result)) + return false; + } + } + + const bool show_types = m_options.show_types; + const bool show_summary = m_options.show_summary; + const bool debug = m_options.debug; + + + if (expr == NULL) + expr = command; + + if (target_triple) + { + ClangExpressionDeclMap expr_decl_map(&exe_ctx); + + ClangExpression clang_expr(target_triple.AsCString(), &expr_decl_map); + + unsigned num_errors = clang_expr.ParseExpression (expr, result.GetErrorStream()); + + if (num_errors == 0) + { + StreamString dwarf_opcodes; + dwarf_opcodes.SetByteOrder(eByteOrderHost); + dwarf_opcodes.GetFlags().Set(Stream::eBinary); + ClangExpressionVariableList expr_local_vars; + clang_expr.ConvertExpressionToDWARF (expr_local_vars, dwarf_opcodes); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + DataExtractor dwarf_opcodes_data(dwarf_opcodes.GetData(), dwarf_opcodes.GetSize(), eByteOrderHost, 8); + DWARFExpression expr(dwarf_opcodes_data, 0, dwarf_opcodes_data.GetByteSize(), NULL); + expr.SetExpressionLocalVariableList(&expr_local_vars); + expr.SetExpressionDeclMap(&expr_decl_map); + if (debug) + { + output_stream << "Expression parsed ok, dwarf opcodes:"; + output_stream.IndentMore(); + expr.GetDescription(&output_stream, lldb::eDescriptionLevelVerbose); + output_stream.IndentLess(); + output_stream.EOL(); + } + + clang::ASTContext *ast_context = clang_expr.GetASTContext(); + Value expr_result; + Error expr_error; + bool expr_success = expr.Evaluate (&exe_ctx, ast_context, NULL, expr_result, &expr_error); + if (expr_success) + { + lldb::Format format = m_options.format; + + // Resolve any values that are possible + expr_result.ResolveValue(&exe_ctx, ast_context); + + if (expr_result.GetContextType() == Value::eContextTypeInvalid && + expr_result.GetValueType() == Value::eValueTypeScalar && + format == eFormatDefault) + { + // The expression result is just a scalar with no special formatting + expr_result.GetScalar().GetValue (&output_stream, show_types); + output_stream.EOL(); + } + else + { + DataExtractor data; + expr_error = expr_result.GetValueAsData (&exe_ctx, ast_context, data, 0); + if (expr_error.Success()) + { + if (format == eFormatDefault) + format = expr_result.GetValueDefaultFormat (); + + void *clang_type = expr_result.GetValueOpaqueClangQualType(); + if (clang_type) + { + if (show_types) + Type::DumpClangTypeName(&output_stream, clang_type); + + Type::DumpValue ( + &exe_ctx, // The execution context for memory and variable access + ast_context, // The ASTContext that the clang type belongs to + clang_type, // The opaque clang type we want to dump that value of + &output_stream, // Stream to dump to + format, // Format to use when dumping + data, // A buffer containing the bytes for the clang type + 0, // Byte offset within "data" where value is + data.GetByteSize(), // Size in bytes of the value we are dumping + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Show types? + show_summary, // Show summary? + debug, // Debug logging output? + UINT32_MAX); // Depth to dump in case this is an aggregate type + } + else + { + data.Dump(&output_stream, // Stream to dump to + 0, // Byte offset within "data" + format, // Format to use when dumping + data.GetByteSize(), // Size in bytes of each item we are dumping + 1, // Number of items to dump + UINT32_MAX, // Number of items per line + LLDB_INVALID_ADDRESS, // Invalid address, don't show any offset/address context + 0, // Bitfield bit size + 0); // Bitfield bit offset + } + output_stream.EOL(); + } + else + { + result.AppendError(expr_error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendError (expr_error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("invalid target triple"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); +} + +lldb::OptionDefinition +CommandObjectExpression::CommandOptions::g_option_table[] = +{ +{ 0, true, "language", 'l', required_argument, NULL, 0, "[c|c++|objc|objc++]", "Sets the language to use when parsing the expression."}, +{ 0, false, "format", 'f', required_argument, NULL, 0, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]", "Specify the format that the expression output should use."}, +{ 0, false, "debug", 'g', no_argument, NULL, 0, NULL, "Enable verbose debug logging of the expression parsing and evaluation."}, +{ 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + diff --git a/lldb/source/Commands/CommandObjectExpression.h b/lldb/source/Commands/CommandObjectExpression.h new file mode 100644 index 000000000000..c67ba745af21 --- /dev/null +++ b/lldb/source/Commands/CommandObjectExpression.h @@ -0,0 +1,105 @@ +//===-- 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/Core/Options.h" +#include "lldb/Core/Language.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +class CommandObjectExpression : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + Language language; + lldb::Encoding encoding; + lldb::Format format; + bool debug; + bool show_types; + bool show_summary; + }; + + CommandObjectExpression (); + + virtual + ~CommandObjectExpression (); + + virtual + Options * + GetOptions (); + + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + WantsRawCommandString() { return true; } + + virtual bool + ExecuteRawCommandString (const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +protected: + + static size_t + MultiLineExpressionCallback (void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + bool + EvaluateExpression (const char *expr, + bool bare, + Stream &output_stream, + Stream &error_stream); + + CommandOptions m_options; + ExecutionContext m_exe_ctx; + 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/lldb/source/Commands/CommandObjectFile.cpp b/lldb/source/Commands/CommandObjectFile.cpp new file mode 100644 index 000000000000..01576c5b6d56 --- /dev/null +++ b/lldb/source/Commands/CommandObjectFile.cpp @@ -0,0 +1,170 @@ +//===-- CommandObjectFile.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectFile.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Timer.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +CommandObjectFile::CommandOptions::CommandOptions() : + Options (), + m_arch () // Breakpoint info defaults to brief descriptions +{ + BuildValidOptionSets(); +} + +CommandObjectFile::CommandOptions::~CommandOptions () +{ +} + +lldb::OptionDefinition +CommandObjectFile::CommandOptions::g_option_table[] = +{ + { 0, false, "arch", 'a', required_argument, NULL, 0, "", "Specify the architecture to launch."}, + { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + +const lldb::OptionDefinition * +CommandObjectFile::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +Error +CommandObjectFile::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + ArchSpec option_arch (option_arg); + if (option_arch.IsValid()) + m_arch = option_arch; + else + error.SetErrorStringWithFormat ("Invalid arch string '%s'.\n", optarg); + } + break; + + default: + error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectFile::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + m_arch.Clear(); +} + +//------------------------------------------------------------------------- +// CommandObjectFile +//------------------------------------------------------------------------- + +CommandObjectFile::CommandObjectFile() : + CommandObject ("file", + "Sets the file to be used as the main executable by the debugger.", + "file [] ") +{ +} + +CommandObjectFile::~CommandObjectFile () +{ +} + +Options * +CommandObjectFile::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectFile::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + const char *file_path = command.GetArgumentAtIndex(0); + Timer scoped_timer(__PRETTY_FUNCTION__, "(dbg) file '%s'", file_path); + const int argc = command.GetArgumentCount(); + if (argc == 1) + { + FileSpec file_spec (file_path); + + if (! file_spec.Exists()) + { + result.AppendErrorWithFormat ("File '%s' does not exist.\n", file_path); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + TargetSP target_sp; + + ArchSpec arch; + if (m_options.m_arch.IsValid()) + arch = m_options.m_arch; + else + { + arch = lldb_private::GetDefaultArchitecture (); + if (!arch.IsValid()) + arch = LLDB_ARCH_DEFAULT; + } + + Error error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file_spec, arch, NULL, true, target_sp); + + if (error.Fail() && !m_options.m_arch.IsValid()) + { + if (arch == LLDB_ARCH_DEFAULT_32BIT) + arch = LLDB_ARCH_DEFAULT_64BIT; + else + arch = LLDB_ARCH_DEFAULT_32BIT; + error = Debugger::GetSharedInstance().GetTargetList().CreateTarget (file_spec, arch, NULL, true, target_sp); + } + + if (target_sp) + { + Debugger::GetSharedInstance().GetTargetList().SetCurrentTarget(target_sp.get()); + result.AppendMessageWithFormat ("Current executable set to '%s' (%s).\n", file_path, arch.AsCString()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("'%s' takes exactly one executable path argument.\n", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + +} diff --git a/lldb/source/Commands/CommandObjectFile.h b/lldb/source/Commands/CommandObjectFile.h new file mode 100644 index 000000000000..c44f610d970c --- /dev/null +++ b/lldb/source/Commands/CommandObjectFile.h @@ -0,0 +1,79 @@ +//===-- CommandObjectFile.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectFile_h_ +#define liblldb_CommandObjectFile_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Options.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectFile +//------------------------------------------------------------------------- + +class CommandObjectFile : public CommandObject +{ +public: + + CommandObjectFile (); + + virtual + ~CommandObjectFile (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual Options * + GetOptions (); + + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + ArchSpec m_arch; + }; + +private: + CommandOptions m_options; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectFile_h_ diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp new file mode 100644 index 000000000000..78682dc0ca3c --- /dev/null +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -0,0 +1,171 @@ +//===-- 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 "CommandObjectFrame.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Timer.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "CommandObjectThread.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark CommandObjectFrameInfo + +//------------------------------------------------------------------------- +// CommandObjectFrameInfo +//------------------------------------------------------------------------- + +class CommandObjectFrameInfo : public CommandObject +{ +public: + + CommandObjectFrameInfo () : + CommandObject ("frame info", + "Lists information about the currently selected frame in the current thread.", + "frame info", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + ~CommandObjectFrameInfo () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.frame) + { + exe_ctx.frame->Dump (&result.GetOutputStream(), true); + result.GetOutputStream().EOL(); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no current frame"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectFrameSelect + +//------------------------------------------------------------------------- +// CommandObjectFrameSelect +//------------------------------------------------------------------------- + +class CommandObjectFrameSelect : public CommandObject +{ +public: + + CommandObjectFrameSelect () : + CommandObject ("frame select", + "Select the current frame by index in the current thread.", + "frame select ", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + ~CommandObjectFrameSelect () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + ExecutionContext exe_ctx (context->GetExecutionContext()); + if (exe_ctx.thread) + { + if (command.GetArgumentCount() == 1) + { + const char *frame_idx_cstr = command.GetArgumentAtIndex(0); + + const uint32_t num_frames = exe_ctx.thread->GetStackFrameCount(); + const uint32_t frame_idx = Args::StringToUInt32 (frame_idx_cstr, UINT32_MAX, 0); + if (frame_idx < num_frames) + { + exe_ctx.thread->SetCurrentFrameByIndex (frame_idx); + exe_ctx.frame = exe_ctx.thread->GetCurrentFrame ().get(); + + if (exe_ctx.frame) + { + if (DisplayFrameForExecutionContext (exe_ctx.thread, + exe_ctx.frame, + interpreter, + result.GetOutputStream(), + true, + true, + 3, + 3)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + } + } + if (frame_idx == UINT32_MAX) + result.AppendErrorWithFormat ("Invalid frame index: %s.\n", frame_idx_cstr); + else + result.AppendErrorWithFormat ("Frame index (%u) out of range.\n", frame_idx); + } + else + { + result.AppendError ("invalid arguments"); + result.AppendErrorWithFormat ("Usage: %s\n", m_cmd_syntax.c_str()); + } + } + else + { + result.AppendError ("no current thread"); + } + result.SetStatus (eReturnStatusFailed); + return false; + } +}; + +#pragma mark CommandObjectMultiwordFrame + +//------------------------------------------------------------------------- +// CommandObjectMultiwordFrame +//------------------------------------------------------------------------- + +CommandObjectMultiwordFrame::CommandObjectMultiwordFrame (CommandInterpreter *interpreter) : + CommandObjectMultiword ("frame", + "A set of commands for operating on the current thread's frames.", + "frame []") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectFrameInfo ()), "info", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectFrameSelect ()), "select", interpreter); +} + +CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame () +{ +} + diff --git a/lldb/source/Commands/CommandObjectFrame.h b/lldb/source/Commands/CommandObjectFrame.h new file mode 100644 index 000000000000..cb9cafe3a74b --- /dev/null +++ b/lldb/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/Core/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/lldb/source/Commands/CommandObjectHelp.cpp b/lldb/source/Commands/CommandObjectHelp.cpp new file mode 100644 index 000000000000..35e5b2ee5c4b --- /dev/null +++ b/lldb/source/Commands/CommandObjectHelp.cpp @@ -0,0 +1,266 @@ +//===-- 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 "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/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectHelp +//------------------------------------------------------------------------- + +CommandObjectHelp::CommandObjectHelp () : + CommandObject ("help", + "Shows a list of all debugger commands, or give details about specific commands.", + "help []") +{ +} + +CommandObjectHelp::~CommandObjectHelp() +{ +} + + +bool +CommandObjectHelp::OldExecute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + + const int argc = command.GetArgumentCount(); + if (argc > 0) + { + cmd_obj = interpreter->GetCommandObject (command.GetArgumentAtIndex(0), false, false); + if (cmd_obj == NULL) + { + cmd_obj = interpreter->GetCommandObject (command.GetArgumentAtIndex(0), true, false); + if (cmd_obj != NULL) + { + StreamString alias_help_str; + interpreter->GetAliasHelp (command.GetArgumentAtIndex(0), cmd_obj->GetCommandName(), alias_help_str); + result.AppendMessageWithFormat ("'%s' is an alias for %s.\n", command.GetArgumentAtIndex (0), + alias_help_str.GetData()); + } + } + + if (cmd_obj) + { + Stream &output_strm = result.GetOutputStream(); + if (cmd_obj->GetOptions() != NULL) + { + const char * long_help = cmd_obj->GetHelpLong(); + if ((long_help!= NULL) + && strlen (long_help) > 0) + output_strm.Printf ("\n%s", cmd_obj->GetHelpLong()); + else + output_strm.Printf ("\n%s\n", cmd_obj->GetHelp()); + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + cmd_obj->GetOptions()->GenerateOptionUsage (output_strm, cmd_obj); + } + else if (cmd_obj->IsMultiwordObject()) + { + bool done = false; + if (argc > 1) + { + CommandObject::CommandMap::iterator pos; + std::string sub_command = command.GetArgumentAtIndex(1); + pos = ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict.find(sub_command); + if (pos != ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict.end()) + { + CommandObject *sub_cmd_obj = pos->second.get(); + if (sub_cmd_obj->GetOptions() != NULL) + { + output_strm.Printf ("\n%s\n", sub_cmd_obj->GetHelp()); + output_strm.Printf ("\nSyntax: %s\n", sub_cmd_obj->GetSyntax()); + sub_cmd_obj->GetOptions()->GenerateOptionUsage (output_strm, sub_cmd_obj); + done = true; + } + else + { + output_strm.Printf ("\n%s\n", sub_cmd_obj->GetHelp()); + output_strm.Printf ("\nSyntax: %s\n", sub_cmd_obj->GetSyntax()); + done = true; + } + } + } + if (!done) + { + output_strm.Printf ("%s\n", cmd_obj->GetHelp()); + ((CommandObjectMultiword *) cmd_obj)->GenerateHelpText (result, interpreter); + } + } + else + { + const char *long_help = cmd_obj->GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("\n%s", cmd_obj->GetHelpLong()); + else + output_strm.Printf ("\n%s\n", cmd_obj->GetHelp()); + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + } + 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); + } + } + else + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + interpreter->GetHelp(result); + } + return result.Succeeded(); +} + +bool +CommandObjectHelp::Execute (Args &command, CommandContext *context, CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + const int argc = command.GetArgumentCount (); + + // 'help' doesn't take any options or arguments, other than command names. If argc is 0, we show the user + // all commands and aliases. Otherwise every argument must be the name of a command or a sub-command. + + if (argc == 0) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + interpreter->GetHelp (result); // General help, for ALL commands. + } + else + { + // Get command object for the first command argument. Only search built-in command dictionary. + cmd_obj = interpreter->GetCommandObject (command.GetArgumentAtIndex (0), false, false); + if (cmd_obj == NULL) + { + // That failed, so now search in the aliases dictionary, too. + cmd_obj = interpreter->GetCommandObject (command.GetArgumentAtIndex (0), true, false); + } + + if (cmd_obj != NULL) + { + 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 (int i = 1; i < argc && all_okay; ++i) + { + std::string sub_command = command.GetArgumentAtIndex(i); + if (! sub_cmd_obj->IsMultiwordObject ()) + { + all_okay = false; + } + else + { + pos = ((CommandObjectMultiword *) sub_cmd_obj)->m_subcommand_dict.find (sub_command); + if (pos != ((CommandObjectMultiword *) sub_cmd_obj)->m_subcommand_dict.end()) + sub_cmd_obj = pos->second.get(); + else + all_okay = false; + } + } + + if (!all_okay || (sub_cmd_obj == NULL)) + { + std::string cmd_string; + command.GetCommandString (cmd_string); + result.AppendErrorWithFormat + ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n", + cmd_string.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + Stream &output_strm = result.GetOutputStream(); + if (sub_cmd_obj->GetOptions() != NULL) + { + output_strm.Printf ("%s\n", sub_cmd_obj->GetHelp()); + output_strm.Printf ("\nSyntax: %s\n", sub_cmd_obj->GetSyntax()); + sub_cmd_obj->GetOptions()->GenerateOptionUsage (output_strm, sub_cmd_obj); + const char *long_help = sub_cmd_obj->GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("\n%s", long_help); + } + else if (sub_cmd_obj->IsMultiwordObject()) + { + output_strm.Printf ("%s\n", sub_cmd_obj->GetHelp()); + ((CommandObjectMultiword *) sub_cmd_obj)->GenerateHelpText (result, interpreter); + } + else + { + const char *long_help = sub_cmd_obj->GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("%s", long_help); + else + output_strm.Printf ("%s\n", sub_cmd_obj->GetHelp()); + output_strm.Printf ("\nSyntax: %s\n", sub_cmd_obj->GetSyntax()); + } + } + } + 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, + CommandInterpreter *interpreter, + StringList &matches +) +{ + // Return the completions of the commands in the help system: + if (cursor_index == 0) + { + return interpreter->HandleCompletionMatches(input, cursor_index, cursor_char_position, match_start_point, max_return_elements, matches); + } + else + { + CommandObject *cmd_obj = interpreter->GetCommandObject (input.GetArgumentAtIndex(0), true, false); + input.Shift(); + cursor_index--; + return cmd_obj->HandleCompletion (input, cursor_index, cursor_char_position, match_start_point, max_return_elements, interpreter, matches); + } +} diff --git a/lldb/source/Commands/CommandObjectHelp.h b/lldb/source/Commands/CommandObjectHelp.h new file mode 100644 index 000000000000..a8084aa704d0 --- /dev/null +++ b/lldb/source/Commands/CommandObjectHelp.h @@ -0,0 +1,59 @@ +//===-- 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" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectHelp +//------------------------------------------------------------------------- + +class CommandObjectHelp : public CommandObject +{ +public: + + CommandObjectHelp (); + + virtual + ~CommandObjectHelp (); + + bool + OldExecute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectHelp_h_ diff --git a/lldb/source/Commands/CommandObjectImage.cpp b/lldb/source/Commands/CommandObjectImage.cpp new file mode 100644 index 000000000000..ab728e9d32d3 --- /dev/null +++ b/lldb/source/Commands/CommandObjectImage.cpp @@ -0,0 +1,1419 @@ +//===-- CommandObjectImage.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectImage.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandCompletions.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Static Helper functions +//---------------------------------------------------------------------- +static void +DumpModuleArchitecture (Stream &strm, Module *module, uint32_t width) +{ + if (module) + { + if (width) + strm.Printf("%-*s", width, module->GetArchitecture().AsCString()); + else + strm.PutCString(module->GetArchitecture().AsCString()); + } +} + +static void +DumpModuleUUID (Stream &strm, Module *module) +{ + module->GetUUID().Dump (&strm); +} + +static uint32_t +DumpCompileUnitLineTable +( + CommandContext *context, + 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 " << *dynamic_cast (sc.comp_unit) << " in `" + << module->GetFileSpec().GetFilename() << "\n"; + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table) + line_table->GetDescription (&strm, context->GetExecutionContext().process, 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) + { + char fullpath[PATH_MAX]; + if (file_spec_ptr->GetPath(fullpath, sizeof(fullpath))) + { + strm.Printf("%-*s", width, fullpath); + 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 (CommandContext *context, Stream &strm, Module *module) +{ + if (module) + { + ObjectFile *objfile = module->GetObjectFile (); + if (objfile) + { + Symtab *symtab = objfile->GetSymtab(); + if (symtab) + symtab->Dump(&strm, context->GetExecutionContext().process); + } + } +} + +static void +DumpModuleSections (CommandContext *context, Stream &strm, Module *module) +{ + if (module) + { + ObjectFile *objfile = module->GetObjectFile (); + if (objfile) + { + SectionList *section_list = objfile->GetSectionList(); + if (section_list) + section_list->Dump(&strm, context->GetExecutionContext().process, true); + } + } +} + +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 bool +LookupAddressInModule (CommandContext *context, Stream &strm, Module *module, uint32_t resolve_mask, lldb::addr_t raw_addr, lldb::addr_t offset) +{ + if (module) + { + lldb::addr_t addr = raw_addr - offset; + Address so_addr; + SymbolContext sc; + Process *process = context->GetExecutionContext().process; + if (process && process->IsAlive()) + { + if (!process->ResolveLoadAddress (addr, so_addr)) + return false; + else if (so_addr.GetModule() != module) + return false; + } + else + { + if (!module->ResolveFileAddress (addr, so_addr)) + return false; + } + + // If an offset was given, print out the address we ended up looking up + if (offset) + strm.Printf("0x%llx: ", addr); + + ExecutionContextScope *exe_scope = context->GetExecutionContext().GetBestExecutionContextScope(); + if (so_addr.Dump (&strm, exe_scope, Address::DumpStyleSectionNameOffset)) + strm.PutCString(": "); + so_addr.Dump (&strm, exe_scope, Address::DumpStyleResolvedDescription); + return true; + } + + return false; +} + +static uint32_t +LookupSymbolInModule (CommandContext *context, Stream &strm, Module *module, const char *name, bool name_is_regex) +{ + if (module) + { + SymbolContext sc; + + ObjectFile *objfile = module->GetObjectFile (); + if (objfile) + { + Symtab *symtab = objfile->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]); + strm.Indent (); + symbol->Dump (&strm, context->GetExecutionContext().process, i); + } + strm.IndentLess (); + return num_matches; + } + } + } + } + return 0; +} + + +static void +DumpSymbolContextList (CommandContext *context, Stream &strm, SymbolContextList &sc_list, bool prepend_addr) +{ + strm.IndentMore (); + uint32_t i; + const uint32_t num_matches = sc_list.GetSize(); + + for (i=0; iGetExecutionContext().process); + int addr_size = sizeof (addr_t); + Process *process = context->GetExecutionContext().process; + if (process) + addr_size = process->GetAddressByteSize(); + if (vm_addr != LLDB_INVALID_ADDRESS) + strm.Address (vm_addr, addr_size); + else + sc.line_entry.range.GetBaseAddress().Dump (&strm, NULL, Address::DumpStyleSectionNameOffset); + + strm.PutCString(" in "); + } + } + sc.DumpStopContext(&strm, context->GetExecutionContext().process, sc.line_entry.range.GetBaseAddress()); + } + } + strm.IndentLess (); +} + +static uint32_t +LookupFunctionInModule (CommandContext *context, Stream &strm, Module *module, const char *name, bool name_is_regex) +{ + if (module && name && name[0]) + { + SymbolContextList sc_list; + + SymbolVendor *symbol_vendor = module->GetSymbolVendor(); + if (symbol_vendor) + { + uint32_t num_matches = 0; + if (name_is_regex) + { + RegularExpression function_name_regex (name); + num_matches = symbol_vendor->FindFunctions(function_name_regex, true, sc_list); + + } + else + { + ConstString function_name(name); + num_matches = symbol_vendor->FindFunctions(function_name, true, sc_list); + } + + if (num_matches) + { + strm.Indent (); + strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : ""); + DumpFullpath (strm, &module->GetFileSpec(), 0); + strm.PutCString(":\n"); + DumpSymbolContextList (context, strm, sc_list, true); + } + return num_matches; + } + } + return 0; +} + +static uint32_t +LookupFileAndLineInModule (CommandContext *context, Stream &strm, Module *module, const FileSpec &file_spec, uint32_t line, bool check_inlines) +{ + 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 (context, strm, sc_list, true); + return num_matches; + } + } + return 0; + +} + + +//---------------------------------------------------------------------- +// Image symbol table dumping command +//---------------------------------------------------------------------- + +class CommandObjectImageDumpModuleList : public CommandObject +{ +public: + + CommandObjectImageDumpModuleList (const char *name, + const char *help, + const char *syntax) : + CommandObject (name, help, syntax) + { + } + + virtual + ~CommandObjectImageDumpModuleList () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches) + { + // Arguments are the standard module completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (CommandCompletions::eModuleCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + interpreter, + NULL, + matches); + return matches.GetSize(); + } +}; + +class CommandObjectImageDumpSourceFileList : public CommandObject +{ +public: + + CommandObjectImageDumpSourceFileList (const char *name, + const char *help, + const char *syntax) : + CommandObject (name, help, syntax) + { + } + + virtual + ~CommandObjectImageDumpSourceFileList () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + 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 (CommandCompletions::eSourceFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + interpreter, + NULL, + matches); + return matches.GetSize(); + } +}; + + +class CommandObjectImageDumpSymtab : public CommandObjectImageDumpModuleList +{ +public: + CommandObjectImageDumpSymtab () : + CommandObjectImageDumpModuleList ("image dump symtab", + "Dump the symbol table from one or more executable images.", + "image dump symtab [ ...]") + { + } + + virtual + ~CommandObjectImageDumpSymtab () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' 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 uint32_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping symbol table for %u modules.\n", num_modules); + for (uint32_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) + { + FileSpec image_file(arg_cstr); + ModuleList matching_modules; + const size_t num_matching_modules = target->GetImages().FindModules(&image_file, NULL, NULL, NULL, matching_modules); + + if (num_matching_modules > 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(); + } + +}; + +//---------------------------------------------------------------------- +// Image section dumping command +//---------------------------------------------------------------------- +class CommandObjectImageDumpSections : public CommandObjectImageDumpModuleList +{ +public: + CommandObjectImageDumpSections () : + CommandObjectImageDumpModuleList ( + "image dump sections", + "Dump the sections from one or more executable images.", + "image dump sections [ ...]") + { + } + + virtual + ~CommandObjectImageDumpSections () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' 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 uint32_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping sections for %u modules.\n", num_modules); + for (uint32_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) + { + FileSpec image_file(arg_cstr); + ModuleList matching_modules; + const size_t num_matching_modules = target->GetImages().FindModules(&image_file, NULL, NULL, NULL, matching_modules); + + if (num_matching_modules > 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(); + } +}; + +//---------------------------------------------------------------------- +// Image debug symbol dumping command +//---------------------------------------------------------------------- +class CommandObjectImageDumpSymfile : public CommandObjectImageDumpModuleList +{ +public: + CommandObjectImageDumpSymfile () : + CommandObjectImageDumpModuleList ("image dump symfile", + "Dump the debug symbol file for one or more executable images.", + "image dump symfile [ ...]") + { + } + + virtual + ~CommandObjectImageDumpSymfile () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' 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 uint32_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping debug symbols for %u modules.\n", num_modules); + for (uint32_t image_idx = 0; image_idxGetImages().GetModulePointerAtIndex(image_idx))) + num_dumped++; + } + } + else + { + result.AppendError ("the target has no associated executable images"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + FileSpec image_file(arg_cstr); + ModuleList matching_modules; + const size_t num_matching_modules = target->GetImages().FindModules(&image_file, NULL, NULL, NULL, matching_modules); + + if (num_matching_modules > 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(); + } +}; + +//---------------------------------------------------------------------- +// Image debug symbol dumping command +//---------------------------------------------------------------------- +class CommandObjectImageDumpLineTable : public CommandObjectImageDumpSourceFileList +{ +public: + CommandObjectImageDumpLineTable () : + CommandObjectImageDumpSourceFileList ("image dump line-table", + "Dump the debug symbol file for one or more executable images.", + "image dump line-table [ ...]") + { + } + + virtual + ~CommandObjectImageDumpLineTable () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + ExecutionContext exe_ctx(context->GetExecutionContext()); + 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); + const uint32_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + uint32_t num_dumped = 0; + for (uint32_t i = 0; iGetImages().GetModulePointerAtIndex(i), + file_spec, + exe_ctx.process != NULL && exe_ctx.process->IsAlive())) + num_dumped++; + } + if (num_dumped == 0) + result.AppendWarningWithFormat ("No source filenames matched '%s'.\n", arg_cstr); + else + total_num_dumped += num_dumped; + } + } + } + + if (total_num_dumped > 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no source filenames matched any command arguments"); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// Dump multi-word command +//---------------------------------------------------------------------- +class CommandObjectImageDump : public CommandObjectMultiword +{ +public: + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectImageDump(CommandInterpreter *interpreter) : + CommandObjectMultiword ("image dump", + "Dumps information in one or more executable images; 'line-table' expects a source file name", + "image dump [symtab|sections|symfile|line-table] [ ...]") + { + LoadSubCommand (CommandObjectSP (new CommandObjectImageDumpSymtab ()), "symtab", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectImageDumpSections ()), "sections", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectImageDumpSymfile ()), "symfile", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectImageDumpLineTable ()), "line-table", interpreter); + } + + virtual + ~CommandObjectImageDump() + { + } +}; + +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectImageList : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options(), + m_format_array() + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + char short_option = (char) m_getopt_table[option_idx].val; + uint32_t width = 0; + if (option_arg) + width = strtoul (option_arg, NULL, 0); + m_format_array.push_back(std::make_pair(short_option, width)); + Error error; + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + m_format_array.clear(); + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + typedef std::vector< std::pair > FormatWidthCollection; + FormatWidthCollection m_format_array; + }; + + CommandObjectImageList () : + CommandObject ( + "image list", + "List current executable and dependent shared library images.", + "image list []") + { + } + + virtual + ~CommandObjectImageList () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + 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 + const uint32_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + Stream &strm = result.GetOutputStream(); + + for (uint32_t image_idx = 0; image_idxGetImages().GetModulePointerAtIndex(image_idx); + strm.Printf("[%3u] ", image_idx); + + if (m_options.m_format_array.empty()) + { + DumpFullpath(strm, &module->GetFileSpec(), 0); + } + else + { + const size_t num_entries = m_options.m_format_array.size(); + for (size_t i=0; i 0) + strm.PutChar(' '); + char format_char = m_options.m_format_array[i].first; + uint32_t width = m_options.m_format_array[i].second; + switch (format_char) + { + case 'a': + DumpModuleArchitecture (strm, module, width); + break; + + case 'f': + DumpFullpath (strm, &module->GetFileSpec(), width); + break; + + case 'd': + DumpDirectory (strm, &module->GetFileSpec(), width); + break; + + case 'b': + DumpBasename (strm, &module->GetFileSpec(), width); + 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') + DumpBasename(strm, &symbol_file->GetObjectFile()->GetFileSpec(), width); + else + DumpFullpath (strm, &symbol_file->GetObjectFile()->GetFileSpec(), width); + break; + } + } + strm.Printf("%.*s", width, ""); + } + break; + + case 'u': + DumpModuleUUID(strm, module); + break; + + default: + break; + } + } + } + strm.EOL(); + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("the target has no associated executable images"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return result.Succeeded(); + } +protected: + + CommandOptions m_options; +}; + +lldb::OptionDefinition +CommandObjectImageList::CommandOptions::g_option_table[] = +{ +{ 0, false, "arch", 'a', optional_argument, NULL, 0, "", "Display the architecture when listing images."}, +{ 0, false, "uuid", 'u', no_argument, NULL, 0, NULL, "Display the UUID when listing images."}, +{ 0, false, "fullpath", 'f', optional_argument, NULL, 0, "", "Display the fullpath to the image object file."}, +{ 0, false, "directory", 'd', optional_argument, NULL, 0, "", "Display the directory with optional width for the image object file."}, +{ 0, false, "basename", 'b', optional_argument, NULL, 0, "", "Display the basename with optional width for the image object file."}, +{ 0, false, "symfile", 's', optional_argument, NULL, 0, "", "Display the fullpath to the image symbol file with optional width."}, +{ 0, false, "symfile-basename", 'S', optional_argument, NULL, 0, "", "Display the basename to the image symbol file with optional width."}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + + +//---------------------------------------------------------------------- +// Lookup information in images +//---------------------------------------------------------------------- +class CommandObjectImageLookup : public CommandObject +{ +public: + + enum + { + eLookupTypeInvalid = -1, + eLookupTypeAddress = 0, + eLookupTypeSymbol, + eLookupTypeFileLine, // Line is optional + eLookupTypeFunction, + kNumLookupTypes + }; + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options() + { + ResetOptionValues(); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_type = eLookupTypeAddress; + m_addr = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS); + if (m_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("Invalid address string '%s'.\n", option_arg); + break; + + case 'o': + m_offset = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS); + if (m_offset == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("Invalid offset string '%s'.\n", option_arg); + break; + + case 's': + m_str = option_arg; + m_type = eLookupTypeSymbol; + break; + + case 'f': + m_file.SetFile (option_arg); + m_type = eLookupTypeFileLine; + break; + + case 'i': + m_check_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'.\n", option_arg); + else if (m_line_number == 0) + error.SetErrorString ("Zero is an invalid line number."); + m_type = eLookupTypeFileLine; + break; + + case 'n': + m_str = option_arg; + m_type = eLookupTypeFunction; + break; + + case 'r': + m_use_regex = true; + break; + } + + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + 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_check_inlines = true; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::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_check_inlines;// Check for inline entries when looking up by file/line. + }; + + CommandObjectImageLookup () : + CommandObject ( + "image lookup", + "Look up information within executable and dependent shared library images.", + "image lookup [] [...]") + { + } + + virtual + ~CommandObjectImageLookup () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + + bool + LookupInModule (CommandContext *context, Module *module, CommandReturnObject &result, bool &syntax_error) + { + switch (m_options.m_type) + { + case eLookupTypeAddress: + if (m_options.m_addr != LLDB_INVALID_ADDRESS) + { + if (LookupAddressInModule (context, result.GetOutputStream(), module, eSymbolContextEverything, m_options.m_addr, m_options.m_offset)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + case eLookupTypeSymbol: + if (!m_options.m_str.empty()) + { + if (LookupSymbolInModule (context, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + case eLookupTypeFileLine: + if (m_options.m_file) + { + + if (LookupFileAndLineInModule (context, + result.GetOutputStream(), + module, + m_options.m_file, + m_options.m_line_number, + m_options.m_check_inlines)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + } + break; + + case eLookupTypeFunction: + if (!m_options.m_str.empty()) + { + if (LookupFunctionInModule (context, + 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; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' 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) + { + // Dump all sections for all modules images + const uint32_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + for (i = 0; iGetImages().GetModulePointerAtIndex(i), result, syntax_error)) + { + result.GetOutputStream().EOL(); + num_successful_lookups++; + } + } + } + else + { + result.AppendError ("the target has no associated executable images"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != NULL && syntax_error == false; ++i) + { + FileSpec image_file(arg_cstr); + ModuleList matching_modules; + const size_t num_matching_modules = target->GetImages().FindModules(&image_file, NULL, NULL, NULL, matching_modules); + + if (num_matching_modules > 0) + { + for (size_t i=0; i 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +protected: + + CommandOptions m_options; +}; + +lldb::OptionDefinition +CommandObjectImageLookup::CommandOptions::g_option_table[] = +{ +{ 1, true, "address", 'a', required_argument, NULL, 0, "", "Lookup an address in one or more executable images."}, +{ 1, false, "offset", 'o', required_argument, NULL, 0, "", "When looking up an address subtract from any addresses before doing the lookup."}, +{ 2, true, "symbol", 's', required_argument, NULL, 0, "", "Lookup a symbol by name in the symbol tables in one or more executable images."}, +{ 2, false, "regex", 'r', no_argument, NULL, 0, NULL, "The argument for name lookups are regular expressions."}, +{ 3, true, "file", 'f', required_argument, NULL, 0, "", "Lookup a file by fullpath or basename in one or more executable images."}, +{ 3, false, "line", 'l', required_argument, NULL, 0, "", "Lookup a line number in a file (must be used in conjunction with --file)."}, +{ 3, false, "no-inlines", 'i', no_argument, NULL, 0, NULL, "Check inline line entries (must be used in conjunction with --file)."}, +{ 4, true, "function", 'n', required_argument, NULL, 0, "", "Lookup a function by name in the debug symbols in one or more executable images."}, +{ 5, false, "regex", 'r', no_argument, NULL, 0, NULL, "The argument for name lookups are regular expressions."}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + + + + +//---------------------------------------------------------------------- +// CommandObjectImage constructor +//---------------------------------------------------------------------- +CommandObjectImage::CommandObjectImage(CommandInterpreter *interpreter) : + CommandObjectMultiword ("image", + "Access information for one or more executable images.", + "image [dump|list] ...") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectImageDump (interpreter)), "dump", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectImageList ()), "list", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectImageLookup ()), "lookup", interpreter); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectImage::~CommandObjectImage() +{ +} + diff --git a/lldb/source/Commands/CommandObjectImage.h b/lldb/source/Commands/CommandObjectImage.h new file mode 100644 index 000000000000..8863a3649a3a --- /dev/null +++ b/lldb/source/Commands/CommandObjectImage.h @@ -0,0 +1,44 @@ +//===-- CommandObjectImage.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectImage_h_ +#define liblldb_CommandObjectImage_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectImage +//------------------------------------------------------------------------- + +class CommandObjectImage : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectImage(CommandInterpreter *interpreter); + virtual + ~CommandObjectImage(); + +private: + //------------------------------------------------------------------ + // For CommandObjectImage only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectImage); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectImage_h_ diff --git a/lldb/source/Commands/CommandObjectInfo.cpp b/lldb/source/Commands/CommandObjectInfo.cpp new file mode 100644 index 000000000000..f817cc189b45 --- /dev/null +++ b/lldb/source/Commands/CommandObjectInfo.cpp @@ -0,0 +1,32 @@ +//===-- CommandObjectInfo.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectInfo.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectInfo +//------------------------------------------------------------------------- + +CommandObjectInfo::CommandObjectInfo () : +CommandObjectCrossref ("info", "Lists the kinds of objects for which you can get information, and shows the syntax for doing so.", "info") +{ +} + +CommandObjectInfo::~CommandObjectInfo () +{ +} + + diff --git a/lldb/source/Commands/CommandObjectInfo.h b/lldb/source/Commands/CommandObjectInfo.h new file mode 100644 index 000000000000..44f9bd1a3945 --- /dev/null +++ b/lldb/source/Commands/CommandObjectInfo.h @@ -0,0 +1,37 @@ +//===-- CommandObjectInfo.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectInfo_h_ +#define liblldb_CommandObjectInfo_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectCrossref.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectInfo +//------------------------------------------------------------------------- + +class CommandObjectInfo : public CommandObjectCrossref +{ +public: + CommandObjectInfo (); + + virtual + ~CommandObjectInfo (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectInfo_h_ diff --git a/lldb/source/Commands/CommandObjectLog.cpp b/lldb/source/Commands/CommandObjectLog.cpp new file mode 100644 index 000000000000..6b54badd7fcf --- /dev/null +++ b/lldb/source/Commands/CommandObjectLog.cpp @@ -0,0 +1,452 @@ +//===-- 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 "CommandObjectLog.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Options.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Interpreter/CommandContext.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; + + +static LogChannelSP +GetLogChannelPluginForChannel (const char *channel) +{ + std::string log_channel_plugin_name(channel); + log_channel_plugin_name += LogChannel::GetPluginSuffix(); + LogChannelSP log_channel_sp (LogChannel::FindPlugin (log_channel_plugin_name.c_str())); + return log_channel_sp; +} + + +class CommandObjectLogEnable : public CommandObject +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogEnable() : + CommandObject ("log enable", + "Enable logging for a single log channel.", + "log enable [] ") + { + } + + virtual + ~CommandObjectLogEnable() + { + } + + Options * + GetOptions () + { + return &m_options; + } + + virtual bool + Execute (Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + if (args.GetArgumentCount() < 1) + { + result.GetErrorStream() << m_cmd_syntax.c_str(); + } + else + { + Log::Callbacks log_callbacks; + + std::string channel(args.GetArgumentAtIndex(0)); + args.Shift (); // Shift off the channel + StreamSP log_stream_sp; + + if (m_options.log_file.empty()) + { + std::string log_file(""); + LogStreamMap::iterator pos = m_log_streams.find(log_file); + if (pos == m_log_streams.end()) + { + log_stream_sp = Log::GetStreamForSTDOUT (); + if (log_stream_sp) + m_log_streams[log_file] = log_stream_sp; + } + else + log_stream_sp = pos->second; + } + else + { + LogStreamMap::iterator pos = m_log_streams.find(m_options.log_file); + if (pos == m_log_streams.end()) + { + log_stream_sp.reset (new StreamFile (m_options.log_file.c_str(), "w")); + m_log_streams[m_options.log_file] = log_stream_sp; + } + else + log_stream_sp = pos->second; + } + assert (log_stream_sp.get()); + uint32_t log_options = m_options.log_options; + if (log_options == 0) + log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; + if (Log::GetLogChannelCallbacks (channel.c_str(), log_callbacks)) + { + log_callbacks.enable (log_stream_sp, log_options, args, &result.GetErrorStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + { + LogChannelSP log_channel_sp (GetLogChannelPluginForChannel(channel.c_str())); + if (log_channel_sp) + { + if (log_channel_sp->Enable (log_stream_sp, log_options, &result.GetErrorStream(), args)) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", channel.c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", channel.c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + } + return result.Succeeded(); + } + + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options (), + log_file (), + log_options (0) + { + } + + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': log_file = option_arg; 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; + default: + error.SetErrorStringWithFormat ("Unrecognized option '%c'\n", short_option); + break; + } + + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + log_file.clear(); + log_options = 0; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string log_file; + uint32_t log_options; + }; + +protected: + typedef std::map LogStreamMap; + CommandOptions m_options; + LogStreamMap m_log_streams; +}; + +lldb::OptionDefinition +CommandObjectLogEnable::CommandOptions::g_option_table[] = +{ +{ 0, false, "file", 'f', required_argument, NULL, 0, "", "Set the destination file to log to."}, +{ 0, false, "threadsafe", 't', no_argument, NULL, 0, NULL, "Enable thread safe logging to avoid interweaved log lines." }, +{ 0, false, "verbose", 'v', no_argument, NULL, 0, NULL, "Enable verbose logging." }, +{ 0, false, "debug", 'g', no_argument, NULL, 0, NULL, "Enable debug logging." }, +{ 0, false, "sequence", 's', no_argument, NULL, 0, NULL, "Prepend all log lines with an increasing integer sequence id." }, +{ 0, false, "timestamp", 'T', no_argument, NULL, 0, NULL, "Prepend all log lines with a timestamp." }, +{ 0, false, "pid-tid", 'p', no_argument, NULL, 0, NULL, "Prepend all log lines with the process and thread ID that generates the log line." }, +{ 0, false, "thread-name",'n', no_argument, NULL, 0, NULL, "Prepend all log lines with the thread name for the thread that generates the log line." }, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + +class CommandObjectLogDisable : public CommandObject +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogDisable() : + CommandObject ("log disable", + "Disable one or more log channels.", + "log disable [ ...]") + { + } + + virtual + ~CommandObjectLogDisable() + { + } + + virtual bool + Execute (Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + result.GetErrorStream() << m_cmd_syntax.c_str(); + } + else + { + for (size_t i=0; iDisable(); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); + } + } + } + return result.Succeeded(); + } +}; + +class CommandObjectLogList : public CommandObject +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogList() : + CommandObject ("log list", + "List the log categories for one or more log channels.", + "log list [ ...]") + { + } + + virtual + ~CommandObjectLogList() + { + } + + virtual bool + Execute (Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + 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 CommandObject +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogTimer() : + CommandObject ("log timers", + "Enable, disable, dump, and reset LLDB internal performance timers.", + "log timers < enable | disable | dump | reset >") + { + } + + virtual + ~CommandObjectLogTimer() + { + } + + virtual bool + Execute (Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + 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); + } + + } + 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 ("log", + "A set of commands for operating on logs.", + "log []") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectLogEnable), "enable", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectLogDisable), "disable", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectLogList), "list", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectLogTimer), "timers", interpreter); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectLog::~CommandObjectLog() +{ +} + + + + diff --git a/lldb/source/Commands/CommandObjectLog.h b/lldb/source/Commands/CommandObjectLog.h new file mode 100644 index 000000000000..a1ba258ea33f --- /dev/null +++ b/lldb/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/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp new file mode 100644 index 000000000000..91abd81e9fd7 --- /dev/null +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -0,0 +1,680 @@ +//===-- 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 "CommandObjectMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Options.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Read memory from the inferior process +//---------------------------------------------------------------------- +class CommandObjectMemoryRead : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + CommandOptions () : + Options() + { + ResetOptionValues(); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': + error = Args::StringToFormat (option_arg, m_format); + + switch (m_format) + { + default: + break; + + case eFormatBoolean: + if (m_byte_size == 0) + m_byte_size = 1; + if (m_num_per_line == 0) + m_num_per_line = 1; + break; + + case eFormatCString: + if (m_num_per_line == 0) + m_num_per_line = 1; + break; + + case eFormatPointer: + break; + + case eFormatBinary: + case eFormatFloat: + case eFormatOctal: + case eFormatDecimal: + case eFormatEnum: + case eFormatUnicode16: + case eFormatUnicode32: + case eFormatUnsigned: + if (m_byte_size == 0) + m_byte_size = 4; + if (m_num_per_line == 0) + m_num_per_line = 1; + break; + + case eFormatBytes: + case eFormatBytesWithASCII: + case eFormatChar: + case eFormatCharPrintable: + if (m_byte_size == 0) + m_byte_size = 1; + break; + case eFormatComplex: + if (m_byte_size == 0) + m_byte_size = 8; + break; + case eFormatHex: + if (m_byte_size == 0) + m_byte_size = 4; + 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: + break; + } + break; + + case 'l': + m_num_per_line = Args::StringToUInt32 (option_arg, 0); + if (m_num_per_line == 0) + error.SetErrorStringWithFormat("Invalid value for --num-per-line option '%s'. Must be positive integer value.\n", option_arg); + break; + + case 'c': + m_count = Args::StringToUInt32 (option_arg, 0); + if (m_count == 0) + error.SetErrorStringWithFormat("Invalid value for --count option '%s'. Must be positive integer value.\n", option_arg); + break; + + case 's': + m_byte_size = Args::StringToUInt32 (option_arg, 0); + if (m_byte_size == 0) + error.SetErrorStringWithFormat("Invalid value for --size option '%s'. Must be positive integer value.\n", option_arg); + break; + + default: + error.SetErrorStringWithFormat("Unrecognized short option '%c'.\n", short_option); + break; + } + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + m_format = eFormatBytesWithASCII; + m_byte_size = 0; + m_count = 0; + m_num_per_line = 0; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + lldb::Format m_format; + uint32_t m_byte_size; + uint32_t m_count; + uint32_t m_num_per_line; + }; + + CommandObjectMemoryRead () : + CommandObject ("memory read", + "Read memory from the process being debugged.", + "memory read [] []", + eFlagProcessMustBeLaunched) + { + } + + virtual + ~CommandObjectMemoryRead () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + result.AppendError("need a process to read memory"); + result.SetStatus(eReturnStatusFailed); + return false; + } + const size_t argc = command.GetArgumentCount(); + + if (argc == 0 || argc > 2) + { + result.AppendErrorWithFormat ("%s takes 1 or two args.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + size_t item_byte_size = m_options.m_byte_size; + if (item_byte_size == 0) + { + if (m_options.m_format == eFormatPointer) + item_byte_size = process->GetAddressByteSize(); + else + item_byte_size = 1; + } + + size_t item_count = m_options.m_count; + + size_t num_per_line = m_options.m_num_per_line; + if (num_per_line == 0) + { + num_per_line = (16/item_byte_size); + if (num_per_line == 0) + num_per_line = 1; + } + + size_t total_byte_size = m_options.m_count * item_byte_size; + if (total_byte_size == 0) + total_byte_size = 32; + + lldb::addr_t addr = Args::StringToUInt64(command.GetArgumentAtIndex(0), LLDB_INVALID_ADDRESS, 0); + + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendErrorWithFormat("invalid start address string '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (argc == 2) + { + lldb::addr_t end_addr = Args::StringToUInt64(command.GetArgumentAtIndex(1), LLDB_INVALID_ADDRESS, 0); + if (end_addr == LLDB_INVALID_ADDRESS) + { + result.AppendErrorWithFormat("Invalid end address string '%s'.\n", command.GetArgumentAtIndex(1)); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (end_addr <= addr) + { + result.AppendErrorWithFormat("End address (0x%llx) must be greater that the start address (0x%llx).\n", end_addr, addr); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (item_count != 0) + { + result.AppendErrorWithFormat("Specify either the end address (0x%llx) or the count (--count %u), 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; + } + else + { + if (item_count == 0) + item_count = 32; + } + + DataBufferSP data_sp(new DataBufferHeap (total_byte_size, '\0')); + Error error; + size_t bytes_read = process->ReadMemory(addr, data_sp->GetBytes (), data_sp->GetByteSize(), error); + if (bytes_read == 0) + { + result.AppendWarningWithFormat("Read from 0x%llx failed.\n", addr); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (bytes_read < total_byte_size) + result.AppendWarningWithFormat("Not all bytes (%u/%u) were able to be read from 0x%llx.\n", bytes_read, total_byte_size, addr); + + result.SetStatus(eReturnStatusSuccessFinishResult); + DataExtractor data(data_sp, process->GetByteOrder(), process->GetAddressByteSize()); + + Stream &output_stream = result.GetOutputStream(); + data.Dump(&output_stream, + 0, + m_options.m_format, + item_byte_size, + item_count, + num_per_line, + addr, + 0, + 0); + output_stream.EOL(); + return true; + } + +protected: + CommandOptions m_options; +}; + +lldb::OptionDefinition +CommandObjectMemoryRead::CommandOptions::g_option_table[] = +{ + { 0, false, "format", 'f', required_argument, NULL, 0, "", "The format that will be used to display the memory. Defaults to bytes with ASCII (--format=Y)."}, + { 0, false, "size", 's', required_argument, NULL, 0, "","The size in bytes to use when displaying with the selected format."}, + { 0, false, "num-per-line", 'l', required_argument, NULL, 0, "", "The number of items per line to display."}, + { 0, false, "count", 'c', required_argument, NULL, 0, "", "The number of total items to display."}, + { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + +//---------------------------------------------------------------------- +// Write memory to the inferior process +//---------------------------------------------------------------------- +class CommandObjectMemoryWrite : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + CommandOptions () : + Options() + { + ResetOptionValues(); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + switch (short_option) + { + case 'f': + error = Args::StringToFormat (option_arg, m_format); + break; + + case 's': + m_byte_size = Args::StringToUInt32 (option_arg, 0); + if (m_byte_size == 0) + error.SetErrorStringWithFormat("Invalid value for --size option '%s'. Must be positive integer value.\n", option_arg); + break; + + + default: + error.SetErrorStringWithFormat("Unrecognized short option '%c'\n", short_option); + break; + } + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + m_format = eFormatBytes; + m_byte_size = 1; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + lldb::Format m_format; + uint32_t m_byte_size; + }; + + CommandObjectMemoryWrite () : + CommandObject ("memory write", + "Write memory to the process being debugged.", + "memory write [] [value1 value2 ...]", + eFlagProcessMustBeLaunched) + { + } + + virtual + ~CommandObjectMemoryWrite () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + 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; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + result.AppendError("need a process to read memory"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const size_t argc = command.GetArgumentCount(); + + if (argc < 2) + { + result.AppendErrorWithFormat ("%s takes an address and at least one value.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + size_t item_byte_size = m_options.m_byte_size ? m_options.m_byte_size : 1; + StreamString buffer (Stream::eBinary, + process->GetAddressByteSize(), + process->GetByteOrder()); + + lldb::addr_t addr = Args::StringToUInt64(command.GetArgumentAtIndex(0), LLDB_INVALID_ADDRESS, 0); + + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendErrorWithFormat("Invalid address string '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus(eReturnStatusFailed); + return false; + } + command.Shift(); // shift off the address argument + uint64_t uval64; + int64_t sval64; + bool success = false; + const uint32_t num_value_args = command.GetArgumentCount(); + uint32_t i; + for (i=0; iWriteMemory (addr, value_str, len, error) == len) + { + addr += len; + } + else + { + result.AppendErrorWithFormat ("Memory write to 0x%llx 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 %lli is too large or small to fit in a %u 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 %llu is too large to fit in a %u 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 %llo is too large to fit in a %u 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().data(), buffer.GetString().size(), error) == buffer.GetString().size()) + return true; + else + { + result.AppendErrorWithFormat ("Memory write to 0x%llx failed: %s.\n", addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + return true; + } + +protected: + CommandOptions m_options; +}; + +lldb::OptionDefinition +CommandObjectMemoryWrite::CommandOptions::g_option_table[] = +{ + { 0, false, "format", 'f', required_argument, NULL, 0, "", "The format value types that will be decoded and written to memory."}, + { 0, false, "size", 's', required_argument, NULL, 0, "","The size in bytes of the values to write to memory."}, + { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectMemory +//------------------------------------------------------------------------- + +CommandObjectMemory::CommandObjectMemory (CommandInterpreter *interpreter) : + CommandObjectMultiword ("memory", + "A set of commands for operating on a memory.", + "memory []") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectMemoryRead ()), "read", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectMemoryWrite ()), "write", interpreter); +} + +CommandObjectMemory::~CommandObjectMemory () +{ +} diff --git a/lldb/source/Commands/CommandObjectMemory.h b/lldb/source/Commands/CommandObjectMemory.h new file mode 100644 index 000000000000..a46654015102 --- /dev/null +++ b/lldb/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/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp new file mode 100644 index 000000000000..ea0f6aff5eb7 --- /dev/null +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -0,0 +1,833 @@ +//===-- 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 "CommandObjectProcess.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Options.h" +#include "lldb/Core/State.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectProcessLaunch +//------------------------------------------------------------------------- + +class CommandObjectProcessLaunch : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options() + { + // Keep default values of all options in one place: ResetOptionValues () + ResetOptionValues (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': stop_at_entry = true; break; + case 'e': stderr_path = option_arg; break; + case 'i': stdin_path = option_arg; break; + case 'o': stdout_path = option_arg; break; + case 'p': plugin_name = option_arg; break; + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + + } + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + stop_at_entry = false; + stdin_path.clear(); + stdout_path.clear(); + stderr_path.clear(); + plugin_name.clear(); + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool stop_at_entry; + std::string stderr_path; + std::string stdin_path; + std::string stdout_path; + std::string plugin_name; + + }; + + CommandObjectProcessLaunch () : + CommandObject ("process launch", + "Launches the executable in the debugger.", + "process launch [] []") + { + } + + + ~CommandObjectProcessLaunch () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + bool + Execute (Args& launch_args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + bool synchronous_execution = interpreter->GetSynchronous (); + // bool launched = false; + // bool stopped_after_launch = false; + + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // If our listener is NULL, users aren't allows to launch + Listener *listener = interpreter->GetListener(); + if (listener == NULL) + { + result.AppendError ("operation not allowed through the command interpreter"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + char filename[PATH_MAX]; + Module *exe_module = target->GetExecutableModule().get(); + exe_module->GetFileSpec().GetPath(filename, sizeof(filename)); + + Process *process = context->GetExecutionContext().process; + if (process) + { + if (process->IsAlive()) + { + result.AppendErrorWithFormat ("Process %u is currently being debugged, kill the process before running again.\n", + process->GetID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + const char *plugin_name; + if (!m_options.plugin_name.empty()) + plugin_name = m_options.plugin_name.c_str(); + else + plugin_name = NULL; + + process = target->CreateProcess (*listener, plugin_name).get(); + + const Args *environment = interpreter->GetEnvironmentVariables(); + const Args *run_args = interpreter->GetProgramArguments(); + + // There are two possible sources of args to be passed to the process upon launching: Those the user + // typed at the run command (launch_args); or those the user pre-set in the run-args variable (run_args). + + // If launch_args is empty, use run_args. + if (launch_args.GetArgumentCount() == 0) + { + if (run_args != NULL) + launch_args.AppendArguments (*run_args); + } + else + { + // launch-args was not empty; use that, AND re-set run-args to contains launch-args values. + StateVariable *run_args_var = interpreter->GetStateVariable ("run-args"); + if (run_args_var != NULL) + { + run_args_var->ArrayClearValues(); + run_args_var->GetArgs().AppendArguments (launch_args); + } + } + + + if (process) + { + const char *archname = exe_module->GetArchitecture().AsCString(); + + const char * stdin_path = NULL; + const char * stdout_path = NULL; + const char * stderr_path = NULL; + + if (!(m_options.stdin_path.empty() && + m_options.stdout_path.empty() && + m_options.stderr_path.empty())) + { + stdin_path = m_options.stdin_path.empty() ? "/dev/null" : m_options.stdin_path.c_str(); + stdout_path = m_options.stdout_path.empty() ? "/dev/null" : m_options.stdout_path.c_str(); + stderr_path = m_options.stderr_path.empty() ? "/dev/null" : m_options.stderr_path.c_str(); + } + + Error error (process->Launch (launch_args.GetConstArgumentVector(), + environment ? environment->GetConstArgumentVector() : NULL, + stdin_path, + stdout_path, + stderr_path)); + + if (error.Success()) + { + result.AppendMessageWithFormat ("Launching '%s' (%s)\n", filename, archname); + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + if (m_options.stop_at_entry == false) + { + StateType state = process->WaitForProcessToStop (NULL); + + if (state == eStateStopped) + { + // Call continue_command. + CommandReturnObject continue_result; + interpreter->HandleCommand("process continue", false, continue_result); + } + + if (synchronous_execution) + { + result.SetDidChangeProcessState (true); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + } + else + { + result.AppendErrorWithFormat ("Process launch failed: %s", + error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("Process launch failed: unable to create a process object.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } + +protected: + + CommandOptions m_options; +}; + + +lldb::OptionDefinition +CommandObjectProcessLaunch::CommandOptions::g_option_table[] = +{ +{ 0, false, "stop-at-entry", 's', no_argument, NULL, 0, NULL, "Stop at the entry point of the program when launching a process."}, +{ 0, false, "stdin", 'i', required_argument, NULL, 0, "", "Redirect stdin for the process to ."}, +{ 0, false, "stdout", 'o', required_argument, NULL, 0, "", "Redirect stdout for the process to ."}, +{ 0, false, "stderr", 'e', required_argument, NULL, 0, "", "Redirect stderr for the process to ."}, +{ 0, false, "plugin", 'p', required_argument, NULL, 0, "", "Name of the process plugin you want to use."}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessAttach +//------------------------------------------------------------------------- + +class CommandObjectProcessAttach : public CommandObject +{ +public: + + CommandObjectProcessAttach () : + CommandObject ("process attach", + "Attaches to a process.", + "process attach ") + { + SetHelpLong("Currently, you must set the executable file before you can attach " + "to a process.\n"); + } + + ~CommandObjectProcessAttach () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // If our listener is NULL, users aren't allows to launch + Listener *listener = interpreter->GetListener(); + if (listener == NULL) + { + result.AppendError ("operation not allowed through the command interpreter"); + result.SetStatus (eReturnStatusFailed); + return false; + } + Process *process = context->GetExecutionContext().process; + if (process) + { + if (process->IsAlive()) + { + result.AppendErrorWithFormat ("Process %u is currently being debugged, kill the process before attaching.\n", process->GetID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (command.GetArgumentCount()) + { + result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: \n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + const char *plugin_name = NULL; + + if (!m_options.plugin_name.empty()) + plugin_name = m_options.plugin_name.c_str(); + + process = target->CreateProcess (*listener, plugin_name).get(); + + if (process) + { + Error error; + int attach_pid = m_options.pid; + + if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + error = process->Attach (attach_pid); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + else + { + result.AppendErrorWithFormat ("Attaching to process %i failed: %s.\n", + attach_pid, + error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else if (!m_options.name.empty()) + { + error = process->Attach (m_options.name.c_str(), m_options.waitfor); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + else + { + if (m_options.waitfor) + result.AppendErrorWithFormat ("Waiting for a process to launch named '%s': %s\n", + m_options.name.c_str(), + error.AsCString()); + else + result.AppendErrorWithFormat ("Failed to a process named '%s': %s\n", + m_options.name.c_str(), + error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + } + } + return result.Succeeded(); + } + + Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options() + { + // Keep default values of all options in one place: ResetOptionValues () + ResetOptionValues (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + bool success = false; + switch (short_option) + { + case 'p': + pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success); + if (!success || pid == LLDB_INVALID_PROCESS_ID) + { + error.SetErrorStringWithFormat("Invalid process ID '%s'.\n", option_arg); + } + break; + + case 'P': + plugin_name = option_arg; + break; + + case 'n': + name.assign(option_arg); + break; + + case 'w': + waitfor = true; + break; + + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + } + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + pid = LLDB_INVALID_PROCESS_ID; + name.clear(); + waitfor = false; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + lldb::pid_t pid; + std::string plugin_name; + std::string name; + bool waitfor; + }; + +protected: + + CommandOptions m_options; +}; + + +lldb::OptionDefinition +CommandObjectProcessAttach::CommandOptions::g_option_table[] = +{ +{ 0, false, "pid", 'p', required_argument, NULL, 0, "", "The process ID of an existing process to attach to."}, +{ 0, false, "plugin", 'P', required_argument, NULL, 0, "", "Name of the process plugin you want to use."}, +{ 1, true, "name", 'n', required_argument, NULL, 0, "", "The name of the process to attach to."}, +{ 1, false, "waitfor", 'w', no_argument, NULL, 0, NULL, "Wait for the the process with to launch."}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessContinue +//------------------------------------------------------------------------- + +class CommandObjectProcessContinue : public CommandObject +{ +public: + + CommandObjectProcessContinue () : + CommandObject ("process continue", + "Continues execution all threads in the current process.", + "process continue", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + + ~CommandObjectProcessContinue () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + bool synchronous_execution = interpreter->GetSynchronous (); + + if (process == NULL) + { + result.AppendError ("no process to continue"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + 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; + } + + 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 ("Resuming process %i\n", process->GetID()); + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %i %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(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessDetach +//------------------------------------------------------------------------- + +class CommandObjectProcessDetach : public CommandObject +{ +public: + + CommandObjectProcessDetach () : + CommandObject ("process detach", + "Detaches from the current process being debugged.", + "process detach", + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessDetach () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + result.AppendError ("must have a valid process in order to detach"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Error error (process->Detach()); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("Detach failed: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessSignal +//------------------------------------------------------------------------- + +class CommandObjectProcessSignal : public CommandObject +{ +public: + + CommandObjectProcessSignal () : + CommandObject ("process signal", + "Sends a UNIX signal to the current process being debugged.", + "process signal ") + { + } + + ~CommandObjectProcessSignal () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + result.AppendError ("no process to signal"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 1) + { + int signo = Args::StringToSInt32(command.GetArgumentAtIndex(0), -1, 0); + if (signo == -1) + { + 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: \n", m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessInterrupt +//------------------------------------------------------------------------- + +class CommandObjectProcessInterrupt : public CommandObject +{ +public: + + + CommandObjectProcessInterrupt () : + CommandObject ("process interrupt", + "Interrupts the current process being debugged.", + "process interrupt", + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessInterrupt () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + result.AppendError ("no process to halt"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + Error error(process->Halt ()); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + + // Maybe we should add a "SuspendThreadPlans so we + // can halt, and keep in place all the current thread plans. + process->GetThreadList().DiscardThreadPlans(); + } + else + { + result.AppendErrorWithFormat ("Failed to halt process: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: \n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessKill +//------------------------------------------------------------------------- + +class CommandObjectProcessKill : public CommandObject +{ +public: + + CommandObjectProcessKill () : + CommandObject ("process kill", + "Terminates the current process being debugged.", + "process kill", + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessKill () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + 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: \n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordProcess +//------------------------------------------------------------------------- + +CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter *interpreter) : + CommandObjectMultiword ("process", + "A set of commands for operating on a process.", + "process []") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectProcessAttach ()), "attach", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectProcessLaunch ()), "launch", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectProcessContinue ()), "continue", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectProcessDetach ()), "detach", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectProcessSignal ()), "signal", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectProcessInterrupt ()), "interrupt", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectProcessKill ()), "kill", interpreter); +} + +CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess () +{ +} + diff --git a/lldb/source/Commands/CommandObjectProcess.h b/lldb/source/Commands/CommandObjectProcess.h new file mode 100644 index 000000000000..9a8d59e70e6f --- /dev/null +++ b/lldb/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/lldb/source/Commands/CommandObjectQuit.cpp b/lldb/source/Commands/CommandObjectQuit.cpp new file mode 100644 index 000000000000..b66f4b108ff4 --- /dev/null +++ b/lldb/source/Commands/CommandObjectQuit.cpp @@ -0,0 +1,48 @@ +//===-- 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 "CommandObjectQuit.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "CommandInterpreter.h" +#include "CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectQuit +//------------------------------------------------------------------------- + +CommandObjectQuit::CommandObjectQuit () : + CommandObject ("quit", "Quits out of the LLDB debugger.", "quit") +{ +} + +CommandObjectQuit::~CommandObjectQuit () +{ +} + +bool +CommandObjectQuit::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + interpreter->BroadcastEvent (CommandInterpreter::eBroadcastBitQuitCommandReceived); + result.SetStatus (eReturnStatusQuit); + return true; +} + diff --git a/lldb/source/Commands/CommandObjectQuit.h b/lldb/source/Commands/CommandObjectQuit.h new file mode 100644 index 000000000000..a45d8a4b51bd --- /dev/null +++ b/lldb/source/Commands/CommandObjectQuit.h @@ -0,0 +1,51 @@ +//===-- 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 +//------------------------------------------------------------------------- + +// SPECIAL NOTE!! The CommandObjectQuit is special, because the actual function to execute +// when the user types 'quit' is passed (via function pointer) to the Command Interpreter when it +// is constructed. The function pointer is then stored in this CommandObjectQuit, and is invoked +// via the CommandObjectQuit::Execute function. This is the only command object that works this +// way; it was done this way because different Command Interpreter callers may want or need different things +// to be done in order to shut down properly. + +class CommandObjectQuit : public CommandObject +{ +public: + + CommandObjectQuit (); + + virtual + ~CommandObjectQuit (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectQuit_h_ diff --git a/lldb/source/Commands/CommandObjectRegister.cpp b/lldb/source/Commands/CommandObjectRegister.cpp new file mode 100644 index 000000000000..c4cb705c725a --- /dev/null +++ b/lldb/source/Commands/CommandObjectRegister.cpp @@ -0,0 +1,231 @@ +//===-- 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 "CommandObjectRegister.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// "register read" +//---------------------------------------------------------------------- +class CommandObjectRegisterRead : public CommandObject +{ +public: + CommandObjectRegisterRead () : + CommandObject ("register read", + "Dump the one or more register values from the current frame.", + "register read [ [ [...]]]", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + virtual + ~CommandObjectRegisterRead () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + StreamString &output_stream = result.GetOutputStream(); + DataExtractor reg_data; + ExecutionContext exe_ctx(context->GetExecutionContext()); + RegisterContext *reg_context = exe_ctx.GetRegisterContext (); + + if (reg_context) + { + const RegisterInfo *reg_info = NULL; + if (command.GetArgumentCount() == 0) + { + uint32_t set_idx; + const uint32_t num_register_sets = reg_context->GetRegisterSetCount(); + for (set_idx = 0; set_idx < num_register_sets; ++set_idx) + { + uint32_t unavailable_count = 0; + const RegisterSet * const reg_set = reg_context->GetRegisterSet(set_idx); + output_stream.Printf ("%s:\n", reg_set->name); + output_stream.IndentMore (); + const uint32_t num_registers = reg_set->num_registers; + for (uint32_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) + { + uint32_t reg = reg_set->registers[reg_idx]; + reg_info = reg_context->GetRegisterInfoAtIndex(reg); + if (reg_context->ReadRegisterBytes(reg, reg_data)) + { + output_stream.Indent (); + output_stream.Printf ("%-12s = ", reg_info ? reg_info->name : ""); + reg_data.Dump(&output_stream, 0, reg_info->format, reg_info->byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + output_stream.EOL(); + } + else + { + ++unavailable_count; + } + } + if (unavailable_count) + { + output_stream.Indent (); + output_stream.Printf("%u registers were unavailable.\n", unavailable_count); + } + output_stream.IndentLess (); + output_stream.EOL(); + } + } + else + { + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + reg_info = reg_context->GetRegisterInfoByName(arg_cstr); + + if (reg_info) + { + output_stream.Printf("%-12s = ", reg_info->name); + if (reg_context->ReadRegisterBytes(reg_info->reg, reg_data)) + { + reg_data.Dump(&output_stream, 0, reg_info->format, reg_info->byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + } + else + { + output_stream.PutCString ("error: unavailable"); + } + output_stream.EOL(); + } + else + { + result.AppendErrorWithFormat ("Invalid register name '%s'.\n", arg_cstr); + } + } + } + } + else + { + result.AppendError ("no current frame"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +//---------------------------------------------------------------------- +// "register write" +//---------------------------------------------------------------------- +class CommandObjectRegisterWrite : public CommandObject +{ +public: + CommandObjectRegisterWrite () : + CommandObject ("register write", + "Modify a single register value.", + "register write ", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + virtual + ~CommandObjectRegisterWrite () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + DataExtractor reg_data; + ExecutionContext exe_ctx(context->GetExecutionContext()); + RegisterContext *reg_context = exe_ctx.GetRegisterContext (); + + if (reg_context) + { + 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); + const RegisterInfo *reg_info = reg_context->GetRegisterInfoByName(reg_name); + + if (reg_info) + { + Scalar scalar; + Error error(scalar.SetValueFromCString (value_str, reg_info->encoding, reg_info->byte_size)); + if (error.Success()) + { + if (reg_context->WriteRegisterValue(reg_info->reg, scalar)) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + } + else + { + result.AppendErrorWithFormat ("Failed to write register '%s' with value '%s': %s\n", + reg_name, + value_str, + error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("Register not found for '%s'.\n", reg_name); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendError ("no current frame"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +//---------------------------------------------------------------------- +// CommandObjectRegister constructor +//---------------------------------------------------------------------- +CommandObjectRegister::CommandObjectRegister(CommandInterpreter *interpreter) : + CommandObjectMultiword ("register", + "Access thread registers.", + "register [read|write] ...") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectRegisterRead ()), "read", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectRegisterWrite ()), "write", interpreter); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegister::~CommandObjectRegister() +{ +} diff --git a/lldb/source/Commands/CommandObjectRegister.h b/lldb/source/Commands/CommandObjectRegister.h new file mode 100644 index 000000000000..740bc5424e46 --- /dev/null +++ b/lldb/source/Commands/CommandObjectRegister.h @@ -0,0 +1,44 @@ +//===-- 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/lldb/source/Commands/CommandObjectRemove.cpp b/lldb/source/Commands/CommandObjectRemove.cpp new file mode 100644 index 000000000000..28736cd16ec0 --- /dev/null +++ b/lldb/source/Commands/CommandObjectRemove.cpp @@ -0,0 +1,89 @@ +//===-- CommandObjectRemove.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectRemove.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; + +//------------------------------------------------------------------------- +// CommandObjectRemove +//------------------------------------------------------------------------- + +CommandObjectRemove::CommandObjectRemove () : + CommandObject ("remove", + "Allows the user to remove/delete user-defined command functions (script functions).", + "remove ") +{ +} + +CommandObjectRemove::~CommandObjectRemove() +{ +} + + +bool +CommandObjectRemove::Execute (Args& args, CommandContext *context, CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + + if (args.GetArgumentCount() != 0) + { + const char *command_name = args.GetArgumentAtIndex(0); + cmd_obj = interpreter->GetCommandObject(command_name); + if (cmd_obj) + { + if (interpreter->CommandExists (command_name)) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + else + { + + if (interpreter->RemoveUser (command_name) == false) + { + if (interpreter->UserCommandExists (command_name)) + result.AppendErrorWithFormat ("Unknown error occurred; unable to remove command '%s'.\n", + command_name); + else + result.AppendErrorWithFormat ("'%s' is not a user-defined command/function name.\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 remove with a valid command"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectRemove.h b/lldb/source/Commands/CommandObjectRemove.h new file mode 100644 index 000000000000..4b017a4fbb14 --- /dev/null +++ b/lldb/source/Commands/CommandObjectRemove.h @@ -0,0 +1,44 @@ +//===-- CommandObjectRemove.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectRemove_h_ +#define liblldb_CommandObjectRemove_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectRemove +//------------------------------------------------------------------------- + +class CommandObjectRemove : public CommandObject +{ +public: + + CommandObjectRemove (); + + virtual + ~CommandObjectRemove (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectRemove_h_ diff --git a/lldb/source/Commands/CommandObjectScript.cpp b/lldb/source/Commands/CommandObjectScript.cpp new file mode 100644 index 000000000000..64864be8d09c --- /dev/null +++ b/lldb/source/Commands/CommandObjectScript.cpp @@ -0,0 +1,149 @@ +//===-- 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 "CommandObjectScript.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" + +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Interpreter/ScriptInterpreterNone.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectScript +//------------------------------------------------------------------------- + +CommandObjectScript::CommandObjectScript (ScriptLanguage script_lang) : + CommandObject ("script", + "Passes an expression to the script interpreter for evaluation and returns the results. Drops user into the interactive interpreter if no expressions are given.", + "script []"), + m_script_lang (script_lang), + m_interpreter_ap () +{ +} + +CommandObjectScript::~CommandObjectScript () +{ +} + +bool +CommandObjectScript::ExecuteRawCommandString +( + const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + std::string arg_str (command); + + ScriptInterpreter *script_interpreter = GetInterpreter (); + + if (script_interpreter == NULL) + { + result.AppendError("no script interpeter"); + result.SetStatus (eReturnStatusFailed); + } + + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + FILE *err_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + if (out_fh && err_fh) + { + if (arg_str.empty()) + script_interpreter->ExecuteInterpreterLoop (out_fh, err_fh); + else + script_interpreter->ExecuteOneLine (arg_str, out_fh, err_fh); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + if (out_fh == NULL) + result.AppendError("invalid output file handle"); + else + result.AppendError("invalid error file handle"); + } + return result.Succeeded(); +} + +bool +CommandObjectScript::WantsRawCommandString() +{ + return true; +} + +bool +CommandObjectScript::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + std::string arg_str; + ScriptInterpreter *script_interpreter = GetInterpreter (); + + if (script_interpreter == NULL) + { + result.AppendError("no script interpeter"); + result.SetStatus (eReturnStatusFailed); + } + + const int argc = command.GetArgumentCount(); + for (int i = 0; i < argc; ++i) + arg_str.append(command.GetArgumentAtIndex(i)); + + + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + FILE *err_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + if (out_fh && err_fh) + { + if (arg_str.empty()) + script_interpreter->ExecuteInterpreterLoop (out_fh, err_fh); + else + script_interpreter->ExecuteOneLine (arg_str, out_fh, err_fh); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + if (out_fh == NULL) + result.AppendError("invalid output file handle"); + else + result.AppendError("invalid error file handle"); + } + return result.Succeeded(); +} + + +ScriptInterpreter * +CommandObjectScript::GetInterpreter () +{ + if (m_interpreter_ap.get() == NULL) + { + switch (m_script_lang) + { + case eScriptLanguagePython: + m_interpreter_ap.reset (new ScriptInterpreterPython ()); + break; + + case eScriptLanguageNone: + m_interpreter_ap.reset (new ScriptInterpreterNone ()); + break; + } + } + return m_interpreter_ap.get(); +} diff --git a/lldb/source/Commands/CommandObjectScript.h b/lldb/source/Commands/CommandObjectScript.h new file mode 100644 index 000000000000..7cd57518ff74 --- /dev/null +++ b/lldb/source/Commands/CommandObjectScript.h @@ -0,0 +1,58 @@ +//===-- 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 CommandObject +{ +public: + + CommandObjectScript (lldb::ScriptLanguage script_lang); + + virtual + ~CommandObjectScript (); + + bool WantsRawCommandString(); + + virtual bool + ExecuteRawCommandString (const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + ScriptInterpreter * + GetInterpreter (); + +private: + lldb::ScriptLanguage m_script_lang; + std::auto_ptr m_interpreter_ap; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectScript_h_ diff --git a/lldb/source/Commands/CommandObjectSelect.cpp b/lldb/source/Commands/CommandObjectSelect.cpp new file mode 100644 index 000000000000..f357cd290a21 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSelect.cpp @@ -0,0 +1,32 @@ +//===-- CommandObjectSelect.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectSelect.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectSelect +//------------------------------------------------------------------------- + +CommandObjectSelect::CommandObjectSelect () : + CommandObjectCrossref ("select", "Lists the kinds of objects you can select, and shows syntax for selecting them.", "select") +{ +} + +CommandObjectSelect::~CommandObjectSelect () +{ +} + + diff --git a/lldb/source/Commands/CommandObjectSelect.h b/lldb/source/Commands/CommandObjectSelect.h new file mode 100644 index 000000000000..18a64eba2762 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSelect.h @@ -0,0 +1,37 @@ +//===-- CommandObjectSelect.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSelect_h_ +#define liblldb_CommandObjectSelect_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectCrossref.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSelect +//------------------------------------------------------------------------- + +class CommandObjectSelect : public CommandObjectCrossref +{ +public: + CommandObjectSelect (); + + virtual + ~CommandObjectSelect (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSelect_h_ diff --git a/lldb/source/Commands/CommandObjectSet.cpp b/lldb/source/Commands/CommandObjectSet.cpp new file mode 100644 index 000000000000..46ad049fd1b5 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSet.cpp @@ -0,0 +1,153 @@ +//===-- CommandObjectSet.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectSet.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; + +//------------------------------------------------------------------------- +// CommandObjectSet +//------------------------------------------------------------------------- + +CommandObjectSet::CommandObjectSet () : + CommandObject ("set", + "Allows the user to set or change the value of a single debugger setting variable.", + "set ") +{ +} + +CommandObjectSet::~CommandObjectSet() +{ +} + + +bool +CommandObjectSet::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandInterpreter::VariableMap::iterator pos; + + const int argc = command.GetArgumentCount(); + + if (argc < 1) + { + result.AppendError ("'set' takes at least two arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = command.GetArgumentAtIndex(0); + const char *var_value = command.GetArgumentAtIndex(1); + + if (var_name == NULL || var_name[0] == '\0') + { + result.AppendError ("'set' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + } + else if (var_value == NULL || var_value[0] == '\0') + { + // No value given: Check to see if we're trying to clear an array. + StateVariable *var = interpreter->GetStateVariable (var_name); + if (var != NULL + && var->GetType() == StateVariable::eTypeStringArray) + { + var->ArrayClearValues(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("'set' command requires a valid variable value; No value supplied"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + StateVariable *var = interpreter->GetStateVariable(var_name); + if (var == NULL) + { + result.AppendErrorWithFormat ("'%s' is not a settable internal variable.\n", var_name); + result.SetStatus (eReturnStatusFailed); + } + else + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + if (var->GetType() == StateVariable::eTypeBoolean) + { + bool success = false; + bool new_value = Args::StringToBoolean (var_value, false, &success); + + if (success) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + if (!var->HasVerifyFunction() || var->VerifyValue (interpreter, (void *) &new_value, result)) + var->SetBoolValue (new_value); + } + else + { + result.AppendErrorWithFormat ("Invalid boolean string '%s'.\n", var_value); + result.SetStatus (eReturnStatusFailed); + } + } + else if (var->GetType() == StateVariable::eTypeInteger) + { + bool success = false; + int new_value = Args::StringToSInt32(var_value, -1, 0, &success); + + if (success) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + if (!var->HasVerifyFunction() || var->VerifyValue (interpreter, (void *) &new_value, result)) + var->SetIntValue (new_value); + } + else + { + result.AppendErrorWithFormat ("Invalid boolean string '%s'.\n", var_value); + result.SetStatus (eReturnStatusFailed); + } + } + else if (var->GetType() == StateVariable::eTypeString) + { + if (!var->HasVerifyFunction() || var->VerifyValue (interpreter, (void *) var_value, result)) + var->SetStringValue (var_value); + } + else if (var->GetType() == StateVariable::eTypeStringArray) + { + if (var_value == NULL || var_value[0] == '\0') + var->ArrayClearValues (); + else + { + command.Shift(); // shift off variable name + var->ArrayClearValues(); // clear the old values + var->GetArgs().AppendArguments (command); // set the new values. + } + } + else + { + result.AppendErrorWithFormat ("Variable '%s' has unrecognized type.\n", + var->GetName()); + result.SetStatus (eReturnStatusFailed); + } + } + } + return result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectSet.h b/lldb/source/Commands/CommandObjectSet.h new file mode 100644 index 000000000000..1a3c3dfd1bc3 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSet.h @@ -0,0 +1,44 @@ +//===-- CommandObjectSet.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSet_h_ +#define liblldb_CommandObjectSet_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSet +//------------------------------------------------------------------------- + +class CommandObjectSet : public CommandObject +{ +public: + + CommandObjectSet (); + + virtual + ~CommandObjectSet (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSet_h_ diff --git a/lldb/source/Commands/CommandObjectSettings.cpp b/lldb/source/Commands/CommandObjectSettings.cpp new file mode 100644 index 000000000000..078b699ffdb9 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSettings.cpp @@ -0,0 +1,62 @@ +//===-- 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 "CommandObjectSettings.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; + +//------------------------------------------------------------------------- +// CommandObjectSettings +//------------------------------------------------------------------------- + +CommandObjectSettings::CommandObjectSettings () : + CommandObject ("settings", + "Lists the debugger settings variables available to the user to 'set' or 'show'.", + "settings") +{ +} + +CommandObjectSettings::~CommandObjectSettings() +{ +} + + +bool +CommandObjectSettings::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandInterpreter::VariableMap::iterator pos; + + if (command.GetArgumentCount() != 0) + { + result.AppendError ("'settings' does not take any arguments"); + result.SetStatus (eReturnStatusFailed); + } + else + { + interpreter->ShowVariableHelp (result); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectSettings.h b/lldb/source/Commands/CommandObjectSettings.h new file mode 100644 index 000000000000..674a98b8ca8d --- /dev/null +++ b/lldb/source/Commands/CommandObjectSettings.h @@ -0,0 +1,44 @@ +//===-- 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" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSettings +//------------------------------------------------------------------------- + +class CommandObjectSettings : public CommandObject +{ +public: + + CommandObjectSettings (); + + virtual + ~CommandObjectSettings (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSettings_h_ diff --git a/lldb/source/Commands/CommandObjectShow.cpp b/lldb/source/Commands/CommandObjectShow.cpp new file mode 100644 index 000000000000..be6f6888a99d --- /dev/null +++ b/lldb/source/Commands/CommandObjectShow.cpp @@ -0,0 +1,74 @@ +//===-- CommandObjectShow.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectShow.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; + +//------------------------------------------------------------------------- +// CommandObjectShow +//------------------------------------------------------------------------- + +CommandObjectShow::CommandObjectShow () : + CommandObject ("show", + "Allows the user to see a single debugger setting variable and its value, or lists them all.", + "show []") +{ +} + +CommandObjectShow::~CommandObjectShow() +{ +} + + +bool +CommandObjectShow::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandInterpreter::VariableMap::iterator pos; + + if (command.GetArgumentCount()) + { + // The user requested to see the value of a particular variable. + + const char *var_name = command.GetArgumentAtIndex(0); + StateVariable *var = interpreter->GetStateVariable(var_name); + if (var) + { + var->AppendVariableInformation (result); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("Unrecognized variable '%s'; cannot do 'show' command.\n", var_name); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + // The user didn't specify a particular variable, so show the values of all of them. + interpreter->ShowVariableValues(result); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + + return result.Succeeded(); +} diff --git a/lldb/source/Commands/CommandObjectShow.h b/lldb/source/Commands/CommandObjectShow.h new file mode 100644 index 000000000000..460280a3c552 --- /dev/null +++ b/lldb/source/Commands/CommandObjectShow.h @@ -0,0 +1,44 @@ +//===-- CommandObjectShow.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectShow_h_ +#define liblldb_CommandObjectShow_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectShow +//------------------------------------------------------------------------- + +class CommandObjectShow : public CommandObject +{ +public: + + CommandObjectShow (); + + virtual + ~CommandObjectShow (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectShow_h_ diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp new file mode 100644 index 000000000000..e2c3a0a143fd --- /dev/null +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -0,0 +1,127 @@ +//===-- 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 "CommandObjectSource.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +using namespace lldb; +using namespace lldb_private; + +const char *k_space_characters = "\t\n\v\f\r "; + +//------------------------------------------------------------------------- +// CommandObjectSource +//------------------------------------------------------------------------- + +CommandObjectSource::CommandObjectSource() : + CommandObject ("source", + "Reads in debugger commands from the file and executes them.", + "source ") +{ +} + +CommandObjectSource::~CommandObjectSource () +{ +} + +bool +CommandObjectSource::Execute +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + const int argc = args.GetArgumentCount(); + if (argc == 1) + { + const char *filename = args.GetArgumentAtIndex(0); + bool success = true; + + result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename); + + FileSpec cmd_file (filename); + if (cmd_file.Exists()) + { + STLStringArray commands; + success = cmd_file.ReadFileLines (commands); + + STLStringArray::iterator pos = commands.begin(); + + // Trim out any empty lines or lines that start with the comment + // char '#' + while (pos != commands.end()) + { + bool remove_string = false; + size_t non_space = pos->find_first_not_of (k_space_characters); + if (non_space == std::string::npos) + remove_string = true; // Empty line + else if ((*pos)[non_space] == '#') + remove_string = true; // Comment line that starts with '#' + + if (remove_string) + pos = commands.erase(pos); + else + ++pos; + } + + if (commands.size() > 0) + { + const size_t num_commands = commands.size(); + size_t i; + for (i = 0; iGetPrompt(), commands[i].c_str()); + if (!interpreter->HandleCommand(commands[i].c_str(), false, result)) + break; + } + + if (i < num_commands) + { + result.AppendErrorWithFormat("Aborting source of '%s' after command '%s' failed.\n", filename, commands[i].c_str()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + success = true; + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("File '%s' does not exist.\n", filename); + result.SetStatus (eReturnStatusFailed); + success = false; + } + + if (success) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + result.AppendErrorWithFormat("'%s' takes exactly one executable filename argument.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + +} diff --git a/lldb/source/Commands/CommandObjectSource.h b/lldb/source/Commands/CommandObjectSource.h new file mode 100644 index 000000000000..416e3c02b2cb --- /dev/null +++ b/lldb/source/Commands/CommandObjectSource.h @@ -0,0 +1,48 @@ +//===-- CommandObjectSource.h -----------------------------------*- 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/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSource +//------------------------------------------------------------------------- + +class CommandObjectSource : public CommandObject +{ +public: + + CommandObjectSource (); + + virtual + ~CommandObjectSource (); + + STLStringArray & + GetCommands (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSource_h_ diff --git a/lldb/source/Commands/CommandObjectSourceFile.cpp b/lldb/source/Commands/CommandObjectSourceFile.cpp new file mode 100644 index 000000000000..df70bc9aea33 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSourceFile.cpp @@ -0,0 +1,206 @@ +//===-- CommandObjectSourceFile.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectSourceFile.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Interpreter/CommandCompletions.h" + +using namespace lldb; +using namespace lldb_private; + +CommandObjectSourceFile::CommandOptions::CommandOptions () : + Options() +{ +} + +CommandObjectSourceFile::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectSourceFile::CommandOptions::SetOptionValue (int option_idx, const char *option_arg) +{ + Error error; + const char 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'.\n", option_arg); + break; + + case 'n': + num_lines = Args::StringToUInt32 (option_arg, 0); + if (num_lines == 0) + error.SetErrorStringWithFormat("Invalid line count: '%s'.\n", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + default: + error.SetErrorStringWithFormat("Unrecognized short option '%c'.\n", short_option); + break; + } + + return error; +} + +void +CommandObjectSourceFile::CommandOptions::ResetOptionValues () +{ + Options::ResetOptionValues(); + + file_spec.Clear(); + file_name.clear(); + start_line = 0; + num_lines = 10; +} + +const lldb::OptionDefinition* +CommandObjectSourceFile::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +lldb::OptionDefinition +CommandObjectSourceFile::CommandOptions::g_option_table[] = +{ +{ 0, false, "line", 'l', required_argument, NULL, 0, "", "The line number at which to start the display source."}, +{ 0, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, "", "The file from which to display source."}, +{ 0, false, "count", 'n', required_argument, NULL, 0, "", "The number of source lines to display."}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + + +//------------------------------------------------------------------------- +// CommandObjectSourceFile +//------------------------------------------------------------------------- + +CommandObjectSourceFile::CommandObjectSourceFile() : + CommandObject ("source-file", + "Display source files from the current executable's debug info.", + "source-file [] []") +{ +} + +CommandObjectSourceFile::~CommandObjectSourceFile () +{ +} + + +Options * +CommandObjectSourceFile::GetOptions () +{ + return &m_options; +} + + +bool +CommandObjectSourceFile::Execute +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + const int argc = args.GetArgumentCount(); + + if (argc != 0) + { + result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + + ExecutionContext exe_ctx(context->GetExecutionContext()); + 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 (interpreter->GetSourceManager().DisplayMoreWithLineNumbers (&result.GetOutputStream())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + else + { + if (interpreter->GetSourceManager().DisplaySourceLinesWithLineNumbersUsingLastFile( + m_options.start_line, // Line to display + 0, // Lines before line to display + m_options.num_lines, // Lines after line to display + "", // Don't mark "line" + &result.GetOutputStream())) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + + } + } + else + { + const char *filename = m_options.file_name.c_str(); + Target *target = context->GetTarget(); + if (target == NULL) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + + bool check_inlines = false; + SymbolContextList sc_list; + size_t num_matches = target->GetImages().ResolveSymbolContextForFilePath (filename, + 0, + check_inlines, + eSymbolContextModule | eSymbolContextCompUnit, + sc_list); + if (num_matches > 0) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) + { + if (sc.comp_unit) + { + interpreter->GetSourceManager ().DisplaySourceLinesWithLineNumbers (sc.comp_unit, + m_options.start_line, // Line to display + 0, // Lines before line to display + m_options.num_lines, // Lines after line to display + "", // Don't mark "line" + &result.GetOutputStream()); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + } + } + } + } + + return result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectSourceFile.h b/lldb/source/Commands/CommandObjectSourceFile.h new file mode 100644 index 000000000000..ba12f0f753cd --- /dev/null +++ b/lldb/source/Commands/CommandObjectSourceFile.h @@ -0,0 +1,80 @@ +//===-- CommandObjectSourceFile.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSourceFile_h_ +#define liblldb_CommandObjectSourceFile_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Core/Options.h" +#include "lldb/Core/FileSpec.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSourceFile +//------------------------------------------------------------------------- + +class CommandObjectSourceFile : public CommandObject +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (int option_idx, const char *option_arg); + + void + ResetOptionValues (); + + const lldb::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + FileSpec file_spec; + std::string file_name; + uint32_t start_line; + uint32_t num_lines; + }; + + CommandObjectSourceFile (); + + virtual + ~CommandObjectSourceFile (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual + Options * + GetOptions (); + +protected: + CommandOptions m_options; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSourceFile_h_ diff --git a/lldb/source/Commands/CommandObjectStatus.cpp b/lldb/source/Commands/CommandObjectStatus.cpp new file mode 100644 index 000000000000..501e0b23c84d --- /dev/null +++ b/lldb/source/Commands/CommandObjectStatus.cpp @@ -0,0 +1,97 @@ +//===-- CommandObjectStatus.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectStatus.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "CommandObjectThread.h" + +#include "lldb/Core/State.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectStatus +//------------------------------------------------------------------------- + +CommandObjectStatus::CommandObjectStatus () : + CommandObject ("status", + "Shows the current status and location of executing process.", + "status", + 0) +{ +} + +CommandObjectStatus::~CommandObjectStatus() +{ +} + + +bool +CommandObjectStatus::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + StreamString &output_stream = result.GetOutputStream(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.process) + { + const StateType state = exe_ctx.process->GetState(); + if (StateIsStoppedState(state)) + { + if (state == eStateExited) + { + int exit_status = exe_ctx.process->GetExitStatus(); + const char *exit_description = exe_ctx.process->GetExitDescription(); + output_stream.Printf ("Process %d exited with status = %i (0x%8.8x) %s\n", + exe_ctx.process->GetID(), + exit_status, + exit_status, + exit_description ? exit_description : ""); + } + else + { + output_stream.Printf ("Process %d %s\n", exe_ctx.process->GetID(), StateAsCString (state)); + if (exe_ctx.thread == NULL) + exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (exe_ctx.thread != NULL) + { + DisplayThreadsInfo (interpreter, &exe_ctx, result, true, true); + } + else + { + result.AppendError ("No valid thread found in current process."); + result.SetStatus (eReturnStatusFailed); + } + } + } + } + else + { + result.AppendError ("No current location or status available."); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); +} + diff --git a/lldb/source/Commands/CommandObjectStatus.h b/lldb/source/Commands/CommandObjectStatus.h new file mode 100644 index 000000000000..da5fa7b70970 --- /dev/null +++ b/lldb/source/Commands/CommandObjectStatus.h @@ -0,0 +1,44 @@ +//===-- CommandObjectStatus.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectStatus_h_ +#define liblldb_CommandObjectStatus_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectStatus +//------------------------------------------------------------------------- + +class CommandObjectStatus : public CommandObject +{ +public: + + CommandObjectStatus (); + + ~CommandObjectStatus (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectStatus_h_ diff --git a/lldb/source/Commands/CommandObjectSyntax.cpp b/lldb/source/Commands/CommandObjectSyntax.cpp new file mode 100644 index 000000000000..b1fc42f76372 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSyntax.cpp @@ -0,0 +1,148 @@ +//===-- 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 "CommandObjectSyntax.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/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 () : + CommandObject ("syntax", + "Shows the correct syntax for a given debugger command.", + "syntax ") +{ +} + +CommandObjectSyntax::~CommandObjectSyntax() +{ +} + + +bool +CommandObjectSyntax::OldExecute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandObject *cmd_obj; + + if (command.GetArgumentCount() != 0) + { + cmd_obj = interpreter->GetCommandObject(command.GetArgumentAtIndex(0)); + if (cmd_obj) + { + Stream &output_strm = result.GetOutputStream(); + if (cmd_obj->GetOptions() != NULL) + { + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + //cmd_obj->GetOptions()->GenerateOptionUsage (output_strm, cmd_obj); + 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 + { + result.AppendErrorWithFormat ("'%s' is not a known command.\n", command.GetArgumentAtIndex(0)); + 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(); +} + +bool +CommandObjectSyntax::Execute (Args &command, CommandContext *context, CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + const int argc = command.GetArgumentCount(); + + if (argc > 0) + { + cmd_obj = interpreter->GetCommandObject (command.GetArgumentAtIndex(0)); + bool all_okay = true; + for (int i = 1; i < argc; ++i) + { + std::string sub_command = command.GetArgumentAtIndex (i); + if (! cmd_obj->IsMultiwordObject()) + all_okay = false; + else + { + pos = ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict.find (sub_command); + if (pos != ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict.end()) + cmd_obj = pos->second.get(); + else + 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()); + //cmd_obj->GetOptions()->GenerateOptionUsage (output_strm, cmd_obj); + 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/lldb/source/Commands/CommandObjectSyntax.h b/lldb/source/Commands/CommandObjectSyntax.h new file mode 100644 index 000000000000..e5f5f4e544d7 --- /dev/null +++ b/lldb/source/Commands/CommandObjectSyntax.h @@ -0,0 +1,51 @@ +//===-- 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 CommandObject +{ +public: + + CommandObjectSyntax (); + + virtual + ~CommandObjectSyntax (); + + bool + OldExecute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSyntax_h_ diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp new file mode 100644 index 000000000000..5ea240e301fb --- /dev/null +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -0,0 +1,430 @@ +//===-- 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 "CommandObjectTarget.h" + +// C Includes +#include +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Timer.h" +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark CommandObjectTargetImageSearchPaths + +class CommandObjectTargetImageSearchPathsAdd : public CommandObject +{ +public: + + CommandObjectTargetImageSearchPathsAdd () : + CommandObject ("target image-search-paths add", + "Add new image search paths substitution pairs to the current target.", + "target image-search-paths add [ ] ...") + { + } + + ~CommandObjectTargetImageSearchPathsAdd () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target * target = context->GetTarget(); + if (target) + { + uint32_t argc = command.GetArgumentCount(); + if (argc & 1) + { + result.AppendError ("add requires an even number of arguments"); + result.SetStatus (eReturnStatusFailed); + } + else + { + for (uint32_t i=0; iGetImageSearchPathList().Append(ConstString(from), + ConstString(to), + last_pair); // Notify if this is the last pair + } + else + { + if (from[0]) + result.AppendError (" can't be empty"); + else + result.AppendError (" can't be empty"); + result.SetStatus (eReturnStatusFailed); + } + } + } + } + else + { + result.AppendError ("invalid target"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +class CommandObjectTargetImageSearchPathsClear : public CommandObject +{ +public: + + CommandObjectTargetImageSearchPathsClear () : + CommandObject ("target image-search-paths clear", + "Clears all current image search paths substitution pairs from the current target.", + "target image-search-paths clear") + { + } + + ~CommandObjectTargetImageSearchPathsClear () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target * target = context->GetTarget(); + if (target) + { + bool notify = true; + target->GetImageSearchPathList().Clear(notify); + } + else + { + result.AppendError ("invalid target"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +class CommandObjectTargetImageSearchPathsInsert : public CommandObject +{ +public: + + CommandObjectTargetImageSearchPathsInsert () : + CommandObject ("target image-search-paths insert", + "Inserts a new image search paths substitution pair to the current target at the specified index.", + "target image-search-paths insert [ ] ...") + { + } + + ~CommandObjectTargetImageSearchPathsInsert () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target * target = context->GetTarget(); + if (target) + { + uint32_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); + } + else + { + if (from[0]) + result.AppendError (" can't be empty"); + else + result.AppendError (" can't be empty"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + else + { + result.AppendError ("insert requires at least three arguments"); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + } + else + { + result.AppendError ("invalid target"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +class CommandObjectTargetImageSearchPathsList : public CommandObject +{ +public: + + CommandObjectTargetImageSearchPathsList () : + CommandObject ("target image-search-paths list", + "Lists all current image search paths substitution pairs in the current target.", + "target image-search-paths list") + { + } + + ~CommandObjectTargetImageSearchPathsList () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target * target = context->GetTarget(); + if (target) + { + if (command.GetArgumentCount() != 0) + { + result.AppendError ("list takes no arguments"); + result.SetStatus (eReturnStatusFailed); + return result.Succeeded(); + } + + target->GetImageSearchPathList().Dump(&result.GetOutputStream()); + } + else + { + result.AppendError ("invalid target"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +class CommandObjectTargetImageSearchPathsQuery : public CommandObject +{ +public: + + CommandObjectTargetImageSearchPathsQuery () : + CommandObject ("target image-search-paths query", + "Transforms a path using the first applicable image search path.", + "target image-search-paths query ") + { + } + + ~CommandObjectTargetImageSearchPathsQuery () + { + } + + bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Target * target = context->GetTarget(); + if (target) + { + if (command.GetArgumentCount() != 1) + { + result.AppendError ("query requires one argument"); + 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()); + } + else + { + result.AppendError ("invalid target"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +// TODO: implement the target select later when we start doing multiple targets +//#pragma mark CommandObjectTargetSelect +// +////------------------------------------------------------------------------- +//// CommandObjectTargetSelect +////------------------------------------------------------------------------- +// +//class CommandObjectTargetSelect : public CommandObject +//{ +//public: +// +// CommandObjectTargetSelect () : +// CommandObject ("frame select", +// "Select the current frame by index in the current thread.", +// "frame select ") +// { +// } +// +// ~CommandObjectTargetSelect () +// { +// } +// +// bool +// Execute (Args& command, +// CommandContext *context, +// CommandInterpreter *interpreter, +// CommandReturnObject &result) +// { +// ExecutionContext exe_ctx (context->GetExecutionContext()); +// if (exe_ctx.thread) +// { +// if (command.GetArgumentCount() == 1) +// { +// const char *frame_idx_cstr = command.GetArgumentAtIndex(0); +// +// const uint32_t num_frames = exe_ctx.thread->GetStackFrameCount(); +// const uint32_t frame_idx = Args::StringToUInt32 (frame_idx_cstr, UINT32_MAX, 0); +// if (frame_idx < num_frames) +// { +// exe_ctx.thread->SetCurrentFrameByIndex (frame_idx); +// exe_ctx.frame = exe_ctx.thread->GetCurrentFrame ().get(); +// +// if (exe_ctx.frame) +// { +// if (DisplayFrameForExecutionContext (exe_ctx.thread, +// exe_ctx.frame, +// interpreter, +// result.GetOutputStream(), +// true, +// true, +// 3, +// 3)) +// { +// result.SetStatus (eReturnStatusSuccessFinishResult); +// return result.Succeeded(); +// } +// } +// } +// if (frame_idx == UINT32_MAX) +// result.AppendErrorWithFormat ("Invalid frame index: %s.\n", frame_idx_cstr); +// else +// result.AppendErrorWithFormat ("Frame index (%u) out of range.\n", frame_idx); +// } +// else +// { +// result.AppendError ("invalid arguments"); +// result.AppendErrorWithFormat ("Usage: %s\n", m_cmd_syntax.c_str()); +// } +// } +// else +// { +// result.AppendError ("no current thread"); +// } +// result.SetStatus (eReturnStatusFailed); +// return false; +// } +//}; + + +#pragma mark CommandObjectMultiwordTarget + +//------------------------------------------------------------------------- +// CommandObjectMultiwordImageSearchPaths +//------------------------------------------------------------------------- + +class CommandObjectMultiwordImageSearchPaths : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordImageSearchPaths (CommandInterpreter *interpreter) : + CommandObjectMultiword ("target image-search-paths", + "A set of commands for operating on debugger target image search paths.", + "target image-search-paths []") + { + LoadSubCommand (CommandObjectSP (new CommandObjectTargetImageSearchPathsAdd ()), "add", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectTargetImageSearchPathsClear ()), "clear", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectTargetImageSearchPathsInsert ()), "insert", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectTargetImageSearchPathsList ()), "list", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectTargetImageSearchPathsQuery ()), "query", interpreter); + } + + ~CommandObjectMultiwordImageSearchPaths() + { + } +}; + + +#pragma mark CommandObjectMultiwordTarget + +//------------------------------------------------------------------------- +// CommandObjectMultiwordTarget +//------------------------------------------------------------------------- + +CommandObjectMultiwordTarget::CommandObjectMultiwordTarget (CommandInterpreter *interpreter) : + CommandObjectMultiword ("target", + "A set of commands for operating on debugger targets.", + "target []") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectMultiwordImageSearchPaths (interpreter)), "image-search-paths", interpreter); +} + +CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget () +{ +} + diff --git a/lldb/source/Commands/CommandObjectTarget.h b/lldb/source/Commands/CommandObjectTarget.h new file mode 100644 index 000000000000..cd569e1821d4 --- /dev/null +++ b/lldb/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/Core/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/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp new file mode 100644 index 000000000000..07777a19cfe7 --- /dev/null +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -0,0 +1,1277 @@ +//===-- 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 "CommandObjectThread.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Options.h" +#include "lldb/Core/State.h" +#include "lldb/Core/SourceManager.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.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/ThreadPlanContinue.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/LineEntry.h" + +using namespace lldb; +using namespace lldb_private; + + +bool +lldb_private::DisplayThreadInfo +( + CommandInterpreter *interpreter, + Stream &strm, + Thread *thread, + bool only_threads_with_stop_reason, + bool show_source +) +{ + if (thread) + { + if (only_threads_with_stop_reason) + { + StopReason thread_stop_reason = eStopReasonNone; + Thread::StopInfo thread_stop_info; + if (thread->GetStopInfo(&thread_stop_info)) + { + thread_stop_reason = thread_stop_info.GetStopReason(); + if (thread_stop_reason == eStopReasonNone) + return false; + } + } + + strm.Indent(); + strm.Printf("%c ", thread->GetProcess().GetThreadList().GetCurrentThread().get() == thread ? '*' : ' '); + + // Show one frame with only the first showing source + if (show_source) + { + DisplayFramesForExecutionContext (thread, + interpreter, + strm, + true, + 0, // Start at first frame + 1, // Number of frames to show + false,// Don't show the frame info since we already displayed most of it above... + 1, // Show source for the first frame + 3, // lines of source context before + 3); // lines of source context after + } + else + { + thread->DumpInfo (strm, + true, // Dump the stop reason? + true, // Dump the thread name? + true, // Dump the queue name? + 0); // Display context info for stack frame zero + + strm.EOL(); + } + + return true; + } + return false; +} + +size_t +lldb_private::DisplayThreadsInfo +( + CommandInterpreter *interpreter, + ExecutionContext *exe_ctx, + CommandReturnObject &result, + bool only_threads_with_stop_reason, + bool show_source +) +{ + StreamString strm; + + size_t num_thread_infos_dumped = 0; + + if (!exe_ctx->process) + return 0; + + const size_t num_threads = exe_ctx->process->GetThreadList().GetSize(); + if (num_threads > 0) + { + + for (uint32_t i = 0; i < num_threads; i++) + { + Thread *thread = exe_ctx->process->GetThreadList().GetThreadAtIndex(i).get(); + if (thread) + { + if (DisplayThreadInfo (interpreter, + strm, + thread, + only_threads_with_stop_reason, + show_source)) + ++num_thread_infos_dumped; + } + } + } + + if (num_thread_infos_dumped > 0) + { + if (num_thread_infos_dumped < num_threads) + result.GetOutputStream().Printf("%u of %u threads stopped with reasons:\n", num_thread_infos_dumped, num_threads); + + result.GetOutputStream().GetString().append(strm.GetString()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + return num_thread_infos_dumped; +} + + +size_t +lldb_private::DisplayFramesForExecutionContext +( + Thread *thread, + CommandInterpreter *interpreter, + Stream& strm, + bool ascending, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source, + uint32_t source_lines_before, + uint32_t source_lines_after +) +{ + if (thread == NULL) + return 0; + + size_t num_frames_displayed = 0; + + if (num_frames == 0) + return 0; + + thread->DumpInfo (strm, + true, // Dump the stop reason? + true, // Dump the thread name? + true, // Dump the queue name? + 0); // Dump info for stack frame zero + strm.EOL(); + strm.IndentMore(); + + StackFrameSP frame_sp; + int frame_idx = 0; + + if (ascending) + { + for (frame_idx = first_frame; frame_idx < first_frame + num_frames; ++frame_idx) + { + frame_sp = thread->GetStackFrameAtIndex (frame_idx); + if (frame_sp.get() == NULL) + break; + + if (DisplayFrameForExecutionContext (thread, + frame_sp.get(), + interpreter, + strm, + show_frame_info, + num_frames_with_source > first_frame - frame_idx, + source_lines_before, + source_lines_after) == false) + break; + + ++num_frames_displayed; + } + } + else + { + for (frame_idx = first_frame + num_frames - 1; frame_idx >= first_frame; --frame_idx) + { + frame_sp = thread->GetStackFrameAtIndex (frame_idx); + if (frame_sp == NULL) + break; + + if (DisplayFrameForExecutionContext (thread, + frame_sp.get(), + interpreter, + strm, + show_frame_info, + num_frames_with_source > first_frame - frame_idx, + source_lines_before, + source_lines_after) == false) + break; + + ++num_frames_displayed; + } + } + strm.IndentLess(); + return num_frames_displayed; +} + +bool +lldb_private::DisplayFrameForExecutionContext +( + Thread *thread, + StackFrame *frame, + CommandInterpreter *interpreter, + Stream& strm, + bool show_frame_info, + bool show_source, + uint32_t source_lines_before, + uint32_t source_lines_after +) +{ + // thread and frame must be filled in prior to calling this function + if (thread && frame) + { + if (show_frame_info) + { + strm.Indent(); + frame->Dump (&strm, true); + strm.EOL(); + } + + SymbolContext sc (frame->GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry)); + + if (show_source && sc.comp_unit && sc.line_entry.IsValid()) + { + interpreter->GetSourceManager().DisplaySourceLinesWithLineNumbers ( + sc.line_entry.file, + sc.line_entry.line, + 3, + 3, + "->", + &strm); + + } + return true; + } + return false; +} + + +//------------------------------------------------------------------------- +// CommandObjectThreadBacktrace +//------------------------------------------------------------------------- + +class CommandObjectThreadBacktrace : public CommandObject +{ +public: + + CommandObjectThreadBacktrace () : + CommandObject ("thread backtrace", + "Shows the stack for one or more threads.", + "thread backtrace [] ...", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), + m_ascending (true) + { + } + + ~CommandObjectThreadBacktrace() + { + } + + + bool + Execute + ( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result + ) + { + if (command.GetArgumentCount() == 0) + { + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.thread) + { + bool show_frame_info = true; + uint32_t num_frames_with_source = 0; // Don't show any frasmes with source when backtracing + if (DisplayFramesForExecutionContext (exe_ctx.thread, + interpreter, + result.GetOutputStream(), + m_ascending, + 0, + UINT32_MAX, + show_frame_info, + num_frames_with_source, + 3, + 3)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + else + { + result.AppendError ("invalid thread"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("backtrace doesn't take arguments (for now)"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +protected: + bool m_ascending; +}; + + +typedef enum StepScope +{ + eStepScopeSource, + eStepScopeInstruction +}; + +class CommandObjectThreadStepWithTypeAndScope : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options() + { + // Keep default values of all options in one place: ResetOptionValues () + ResetOptionValues (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) 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'.\n", short_option); + } + break; + case 'm': + { + bool found_one = false; + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + m_run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, &found_one); + if (!found_one) + error.SetErrorStringWithFormat("Invalid enumeration value for option '%c'.\n", short_option); + } + break; + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + + } + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + m_avoid_no_debug = true; + m_run_mode = eOnlyDuringStepping; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + bool m_avoid_no_debug; + RunMode m_run_mode; + }; + + CommandObjectThreadStepWithTypeAndScope (const char *name, + const char *help, + const char *syntax, + uint32_t flags, + StepType step_type, + StepScope step_scope) : + CommandObject (name, help, syntax, flags), + m_step_type (step_type), + m_step_scope (step_scope), + m_options () + { + } + + virtual + ~CommandObjectThreadStepWithTypeAndScope () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + bool synchronous_execution = interpreter->GetSynchronous(); + + if (process == NULL) + { + result.AppendError ("need a valid process to step"); + result.SetStatus (eReturnStatusFailed); + + } + else + { + const uint32_t num_threads = process->GetThreadList().GetSize(); + Thread *thread = NULL; + + if (command.GetArgumentCount() == 0) + { + thread = process->GetThreadList().GetCurrentThread().get(); + if (thread == NULL) + { + result.AppendError ("no current 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, 0, 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 + bool_stop_other_threads = true; + + if (m_step_type == eStepTypeInto) + { + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + ThreadPlan *new_plan; + + if (frame->HasDebugInformation ()) + { + new_plan = thread->QueueThreadPlanForStepRange (abort_other_plans, m_step_type, + frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, + frame->GetSymbolContext(eSymbolContextEverything), + stop_other_threads); + if (new_plan) + { + ThreadPlanStepInRange *real_plan = dynamic_cast (new_plan); + if (real_plan) + { + if (m_options.m_avoid_no_debug) + { + real_plan->GetFlags().Set (ThreadPlanShouldStopHere::eAvoidNoDebug); + } + else + { + real_plan->GetFlags().Clear (ThreadPlanShouldStopHere::eAvoidNoDebug); + } + } + } + } + else + new_plan = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); + + process->GetThreadList().SetCurrentThreadByID (thread->GetID()); + process->Resume (); + } + else if (m_step_type == eStepTypeOver) + { + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + ThreadPlan *new_plan; + + if (frame->HasDebugInformation()) + new_plan = thread->QueueThreadPlanForStepRange (abort_other_plans, + m_step_type, + frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, + frame->GetSymbolContext(eSymbolContextEverything), + stop_other_threads); + else + new_plan = thread->QueueThreadPlanForStepSingleInstruction (true, + abort_other_plans, + bool_stop_other_threads); + + // FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over. + // Maybe there should be a parameter to control this. + new_plan->SetOkayToDiscard(false); + + process->GetThreadList().SetCurrentThreadByID (thread->GetID()); + process->Resume (); + } + else if (m_step_type == eStepTypeTrace) + { + thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); + process->GetThreadList().SetCurrentThreadByID (thread->GetID()); + process->Resume (); + } + else if (m_step_type == eStepTypeTraceOver) + { + thread->QueueThreadPlanForStepSingleInstruction (true, abort_other_plans, bool_stop_other_threads); + process->GetThreadList().SetCurrentThreadByID (thread->GetID()); + process->Resume (); + } + else if (m_step_type == eStepTypeOut) + { + ThreadPlan *new_plan; + + new_plan = thread->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, bool_stop_other_threads, eVoteYes, eVoteNoOpinion); + // FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over. + // Maybe there should be a parameter to control this. + new_plan->SetOkayToDiscard(false); + + process->GetThreadList().SetCurrentThreadByID (thread->GetID()); + process->Resume (); + } + else + { + result.AppendError ("step type is not supported"); + result.SetStatus (eReturnStatusFailed); + } + 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().SetCurrentThreadByID (thread->GetID()); + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %i %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + return result.Succeeded(); + } + +protected: + StepType m_step_type; + StepScope m_step_scope; + CommandOptions m_options; +}; + +static lldb::OptionEnumValueElement +g_tri_running_mode[] = +{ +{ eOnlyThisThread, "thisThread", "Run only this thread"}, +{ eAllThreads, "allThreads", "Run all threads"}, +{ eOnlyDuringStepping, "whileStepping", "Run only this thread while stepping"}, +{ 0, NULL, NULL } +}; + +static lldb::OptionEnumValueElement +g_duo_running_mode[] = +{ +{ eOnlyThisThread, "thisThread", "Run only this thread"}, +{ eAllThreads, "allThreads", "Run all threads"}, +{ 0, NULL, NULL } +}; + +lldb::OptionDefinition +CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] = +{ +{ 0, true, "avoid_no_debug", 'a', required_argument, NULL, 0, "", "Should step-in step over functions with no debug information"}, +{ 0, true, "run_mode", 'm', required_argument, g_tri_running_mode, 0, "", "Determine how to run other threads while stepping this one"}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadContinue +//------------------------------------------------------------------------- + +class CommandObjectThreadContinue : public CommandObject +{ +public: + + CommandObjectThreadContinue () : + CommandObject ("thread continue", + "Continues execution of one or more threads in an active process.", + "thread continue [ ...]", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + + virtual + ~CommandObjectThreadContinue () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + bool synchronous_execution = interpreter->GetSynchronous (); + + if (!context->GetTarget()) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Process *process = context->GetExecutionContext().process; + 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)) + { + const uint32_t num_threads = process->GetThreadList().GetSize(); + uint32_t idx; + const size_t argc = command.GetArgumentCount(); + if (argc > 0) + { + std::vector resume_thread_indexes; + for (uint32_t i=0; iGetThreadList().GetThreadAtIndex(idx).get(); + if (find(resume_thread_indexes.begin(), resume_thread_indexes.end(), idx) != resume_thread_indexes.end()) + { + result.AppendMessageWithFormat ("%u ", idx); + thread->SetResumeState (eStateRunning); + } + else + { + thread->SetResumeState (eStateSuspended); + } + } + result.AppendMessageWithFormat ("in process %i\n", process->GetID()); + } + } + else + { + Thread *current_thread = process->GetThreadList().GetCurrentThread().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 (idx=0; idxGetThreadList().GetThreadAtIndex(idx).get(); + if (thread == current_thread) + { + result.AppendMessageWithFormat ("Resuming thread 0x%4.4x in process %i\n", thread->GetID(), process->GetID()); + thread->SetResumeState (eStateRunning); + } + else + { + thread->SetResumeState (eStateSuspended); + } + } + } + + Error error (process->Resume()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID()); + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %i %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 CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + uint32_t m_thread_idx; + uint32_t m_frame_idx; + + CommandOptions () : + Options(), + m_thread_idx(LLDB_INVALID_THREAD_ID), + m_frame_idx(LLDB_INVALID_FRAME_ID) + { + // Keep default values of all options in one place: ResetOptionValues () + ResetOptionValues (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + + switch (short_option) + { + case 't': + { + uint32_t m_thread_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_INDEX32); + if (m_thread_idx == LLDB_INVALID_INDEX32) + { + error.SetErrorStringWithFormat ("Invalid thread index '%s'.\n", 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'.\n", option_arg); + } + } + break; + case 'm': + { + bool found_one = false; + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + lldb::RunMode run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, &found_one); + + if (!found_one) + error.SetErrorStringWithFormat("Invalid enumeration value for option '%c'.\n", short_option); + else if (run_mode == eAllThreads) + m_stop_others = false; + else + m_stop_others = true; + + } + break; + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + + } + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + m_thread_idx = LLDB_INVALID_THREAD_ID; + m_frame_idx = 0; + m_stop_others = false; + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + uint32_t m_step_thread_idx; + bool m_stop_others; + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + }; + + CommandObjectThreadUntil () : + CommandObject ("thread until", + "Runs the current or specified thread until it reaches a given line number or leaves the current function.", + "thread until [] ", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused), + m_options () + { + } + + + virtual + ~CommandObjectThreadUntil () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + bool synchronous_execution = interpreter->GetSynchronous (); + + if (!context->GetTarget()) + { + result.AppendError ("invalid target, set executable file using 'file' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Process *process = context->GetExecutionContext().process; + 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().GetCurrentThread().get(); + } + else + { + thread = process->GetThreadList().GetThreadAtIndex(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, 0, num_threads); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const bool abort_other_plans = true; + + 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; + } + + ThreadPlan *new_plan; + + 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); + + while (index_ptr <= end_ptr) + { + LineEntry line_entry; + index_ptr = sc.comp_unit->FindLineEntry(index_ptr, line_number, sc.comp_unit, &line_entry); + if (index_ptr == UINT32_MAX) + break; + + addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(process); + if (address != LLDB_INVALID_ADDRESS) + address_list.push_back (address); + index_ptr++; + } + + new_plan = thread->QueueThreadPlanForStepUntil (abort_other_plans, address_list.data(), address_list.size(), m_options.m_stop_others); + new_plan->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().SetCurrentThreadByID (m_options.m_thread_idx); + Error error (process->Resume ()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Resuming process %i\n", process->GetID()); + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %i %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(); + } +protected: + CommandOptions m_options; + +}; + +lldb::OptionDefinition +CommandObjectThreadUntil::CommandOptions::g_option_table[] = +{ +{ 0, true, "frame", 'f', required_argument, NULL, 0, "", "Frame index for until operation - defaults to 0"}, +{ 0, true, "thread", 't', required_argument, NULL, 0, "", "Thread index for the thread for until operation"}, +{ 0, true, "run_mode", 'm', required_argument, g_duo_running_mode, 0, "", "Determine how to run other threads while stepping this one"}, +{ 0, false, NULL, 0, 0, NULL, 0, NULL, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadSelect +//------------------------------------------------------------------------- + +class CommandObjectThreadSelect : public CommandObject +{ +public: + + CommandObjectThreadSelect () : + CommandObject ("thread select", + "Selects a threads as the currently active thread.", + "thread select ", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + + virtual + ~CommandObjectThreadSelect () + { + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + Process *process = context->GetExecutionContext().process; + 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: \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().SetCurrentThreadByID(new_thread->GetID()); + + DisplayThreadInfo (interpreter, + result.GetOutputStream(), + new_thread, + false, + true); + + return result.Succeeded(); + } + +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadList +//------------------------------------------------------------------------- + +CommandObjectThreadList::CommandObjectThreadList (): + CommandObject ("thread list", + "Shows a summary of all current threads in a process.", + "thread list", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) +{ +} + +CommandObjectThreadList::~CommandObjectThreadList() +{ +} + +bool +CommandObjectThreadList::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + StreamString &strm = result.GetOutputStream(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.process) + { + const StateType state = exe_ctx.process->GetState(); + + if (StateIsStoppedState(state)) + { + if (state == eStateExited) + { + int exit_status = exe_ctx.process->GetExitStatus(); + const char *exit_description = exe_ctx.process->GetExitDescription(); + strm.Printf ("Process %d exited with status = %i (0x%8.8x) %s\n", + exe_ctx.process->GetID(), + exit_status, + exit_status, + exit_description ? exit_description : ""); + } + else + { + strm.Printf ("Process %d state is %s\n", exe_ctx.process->GetID(), StateAsCString (state)); + if (exe_ctx.thread == NULL) + exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (exe_ctx.thread != NULL) + { + DisplayThreadsInfo (interpreter, &exe_ctx, result, false, false); + } + else + { + result.AppendError ("no valid thread found in current process"); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendError ("process is currently running"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no current location or status available"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); +} + +//------------------------------------------------------------------------- +// CommandObjectMultiwordThread +//------------------------------------------------------------------------- + +CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter *interpreter) : + CommandObjectMultiword ("thread", + "A set of commands for operating on one or more thread within a running process.", + "thread []") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectThreadBacktrace ()), "backtrace", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectThreadContinue ()), "continue", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectThreadList ()), "list", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectThreadSelect ()), "select", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectThreadUntil ()), "until", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-in", + "Source level single step in in specified thread (current thread, if none specified).", + "thread step-in []", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, + eStepTypeInto, + eStepScopeSource)), + "step-in", interpreter); + + LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-out", + "Source level single step out in specified thread (current thread, if none specified).", + "thread step-out []", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, + eStepTypeOut, + eStepScopeSource)), + "step-out", interpreter); + + LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-over", + "Source level single step over in specified thread (current thread, if none specified).", + "thread step-over []", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, + eStepTypeOver, + eStepScopeSource)), + "step-over", interpreter); + + LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst", + "Single step one instruction in specified thread (current thread, if none specified).", + "thread step-inst []", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, + eStepTypeTrace, + eStepScopeInstruction)), + "step-inst", interpreter); + LoadSubCommand (CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ("thread step-inst-over", + "Single step one instruction in specified thread (current thread, if none specified), stepping over calls.", + "thread step-inst-over []", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused, + eStepTypeTraceOver, + eStepScopeInstruction)), + "step-inst-over", interpreter); +} + +CommandObjectMultiwordThread::~CommandObjectMultiwordThread () +{ +} + + diff --git a/lldb/source/Commands/CommandObjectThread.h b/lldb/source/Commands/CommandObjectThread.h new file mode 100644 index 000000000000..21bba7146268 --- /dev/null +++ b/lldb/source/Commands/CommandObjectThread.h @@ -0,0 +1,87 @@ +//===-- 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 CommandObjectThreadList : public CommandObject +{ +public: + + CommandObjectThreadList (); + + ~CommandObjectThreadList (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); +}; + + +class CommandObjectMultiwordThread : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordThread (CommandInterpreter *interpreter); + + virtual + ~CommandObjectMultiwordThread (); + +}; + + +bool +DisplayThreadInfo (CommandInterpreter *interpreter, + Stream &strm, + Thread *thread, + bool only_threads_with_stop_reason, + bool show_source); + +size_t +DisplayThreadsInfo (CommandInterpreter *interpreter, + ExecutionContext *exe_ctx, + CommandReturnObject &result, + bool only_threads_with_stop_reason, + bool show_source); + +size_t +DisplayFramesForExecutionContext (Thread *thread, + CommandInterpreter *interpreter, + Stream& strm, + bool ascending, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source, + uint32_t source_lines_before, + uint32_t source_lines_after); + +bool +DisplayFrameForExecutionContext (Thread *thread, + StackFrame *frame, + CommandInterpreter *interpreter, + Stream& strm, + bool show_frame_info, + bool show_source, + uint32_t source_lines_before, + uint32_t source_lines_after); + +} // namespace lldb_private + +#endif // liblldb_CommandObjectThread_h_ diff --git a/lldb/source/Commands/CommandObjectTranslate.cpp b/lldb/source/Commands/CommandObjectTranslate.cpp new file mode 100644 index 000000000000..48a10626c8d5 --- /dev/null +++ b/lldb/source/Commands/CommandObjectTranslate.cpp @@ -0,0 +1,75 @@ +//===-- CommandObjectTranslate.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectTranslate.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Options.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectTranslate +//------------------------------------------------------------------------- + +CommandObjectTranslate::CommandObjectTranslate () : + CommandObject ("translate", + "Shows the actual function called for a given debugger command.", + "translate ") +{ +} + +CommandObjectTranslate::~CommandObjectTranslate() +{ +} + + +bool +CommandObjectTranslate::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + CommandObject *cmd_obj; + + if (command.GetArgumentCount() != 0) + { + cmd_obj = interpreter->GetCommandObject(command.GetArgumentAtIndex(0)); + if (cmd_obj) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + result.AppendMessageWithFormat ("%s\n", cmd_obj->Translate()); + } + else + { + result.AppendErrroWithFormat + ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n", + command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("must call translate with a valid command"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); +} diff --git a/lldb/source/Commands/CommandObjectTranslate.h b/lldb/source/Commands/CommandObjectTranslate.h new file mode 100644 index 000000000000..efc3c8b4092b --- /dev/null +++ b/lldb/source/Commands/CommandObjectTranslate.h @@ -0,0 +1,44 @@ +//===-- CommandObjectTranslate.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectTranslate_h_ +#define liblldb_CommandObjectTranslate_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectTranslate +//------------------------------------------------------------------------- + +class CommandObjectTranslate : public CommandObject +{ +public: + + CommandObjectTranslate (); + + virtual + ~CommandObjectTranslate (); + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectTranslate_h_ diff --git a/lldb/source/Commands/CommandObjectUnalias.cpp b/lldb/source/Commands/CommandObjectUnalias.cpp new file mode 100644 index 000000000000..6c2f5085cf8a --- /dev/null +++ b/lldb/source/Commands/CommandObjectUnalias.cpp @@ -0,0 +1,87 @@ +//===-- CommandObjectUnalias.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectUnalias.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; + +//------------------------------------------------------------------------- +// CommandObjectUnalias +//------------------------------------------------------------------------- + +CommandObjectUnalias::CommandObjectUnalias () : + CommandObject ("unalias", + "Allows the user to remove/delete a user-defined command abbreviation.", + "unalias ") +{ +} + +CommandObjectUnalias::~CommandObjectUnalias() +{ +} + + +bool +CommandObjectUnalias::Execute (Args& args, CommandContext *context, CommandInterpreter *interpreter, + CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + + if (args.GetArgumentCount() != 0) + { + const char *command_name = args.GetArgumentAtIndex(0); + cmd_obj = interpreter->GetCommandObject(command_name); + if (cmd_obj) + { + if (interpreter->CommandExists (command_name)) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + else + { + + if (interpreter->RemoveAlias (command_name) == false) + { + if (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(); +} + diff --git a/lldb/source/Commands/CommandObjectUnalias.h b/lldb/source/Commands/CommandObjectUnalias.h new file mode 100644 index 000000000000..5d1cafbcc718 --- /dev/null +++ b/lldb/source/Commands/CommandObjectUnalias.h @@ -0,0 +1,44 @@ +//===-- CommandObjectUnalias.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectUnalias_h_ +#define liblldb_CommandObjectUnalias_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectUnalias +//------------------------------------------------------------------------- + +class CommandObjectUnalias : public CommandObject +{ +public: + + CommandObjectUnalias (); + + virtual + ~CommandObjectUnalias (); + + virtual bool + Execute (Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectUnalias_h_ diff --git a/lldb/source/Commands/CommandObjectVariable.cpp b/lldb/source/Commands/CommandObjectVariable.cpp new file mode 100644 index 000000000000..6bde4829be0a --- /dev/null +++ b/lldb/source/Commands/CommandObjectVariable.cpp @@ -0,0 +1,801 @@ +//===-- CommandObjectVariable.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Options.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.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/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//void +//DumpValueObjectValues (Stream *sout, const char *root_valobj_name, ValueObjectSP& valobj_sp, bool follow_ptrs_and_refs, uint32_t curr_depth, uint32_t max_depth) +//{ +// ValueObject *valobj = valobj_sp.get(); +// if (valobj) +// { +// const char *name_cstr = valobj->GetName().AsCString(NULL); +// const char *val_cstr = valobj->GetValueAsCString(); +// const char *loc_cstr = valobj->GetLocationAsCString(); +// const char *type_cstr = valobj->GetTypeName().AsCString(); +// const char *sum_cstr = valobj->GetSummaryAsCString(); +// const char *err_cstr = valobj->GetError().AsCString(); +// // Indent +// sout->Indent(); +// if (root_valobj_name) +// { +// sout->Printf ("%s = ", root_valobj_name); +// } +// +// if (name_cstr) +// sout->Printf ("%s => ", name_cstr); +// +// sout->Printf ("ValueObject{%u}", valobj->GetID()); +// const uint32_t num_children = valobj->GetNumChildren(); +// +// if (type_cstr) +// sout->Printf (", type = '%s'", type_cstr); +// +// if (loc_cstr) +// sout->Printf (", location = %s", loc_cstr); +// +// sout->Printf (", num_children = %u", num_children); +// +// if (val_cstr) +// sout->Printf (", value = %s", val_cstr); +// +// if (err_cstr) +// sout->Printf (", error = %s", err_cstr); +// +// if (sum_cstr) +// sout->Printf (", summary = %s", sum_cstr); +// +// sout->EOL(); +// bool is_ptr_or_ref = ClangASTContext::IsPointerOrReferenceType (valobj->GetOpaqueClangQualType()); +// if (!follow_ptrs_and_refs && is_ptr_or_ref) +// return; +// +// if (curr_depth < max_depth) +// { +// for (uint32_t idx=0; idxGetChildAtIndex(idx, true)); +// if (child_sp.get()) +// { +// sout->IndentMore(); +// DumpValueObjectValues (sout, NULL, child_sp, follow_ptrs_and_refs, curr_depth + 1, max_depth); +// sout->IndentLess(); +// } +// } +// } +// } +//} + +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectVariableList : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options() + { + ResetOptionValues (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + bool success; + char short_option = (char) m_getopt_table[option_idx].val; + switch (short_option) + { + case 'o': use_objc = true; break; + case 'n': name = option_arg; break; + case 'r': use_regex = true; break; + case 'a': show_args = false; break; + case 'l': show_locals = false; break; + case 'g': show_globals = false; break; + case 't': show_types = false; break; + case 'y': show_summary = false; break; + case 'L': show_location= true; break; + case 'D': debug = true; break; + case 'd': + max_depth = Args::StringToUInt32 (option_arg, UINT32_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid max depth '%s'.\n", option_arg); + break; + + case 'p': + ptr_depth = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid pointer depth '%s'.\n", option_arg); + break; + + case 'G': + { + ConstString const_string (option_arg); + globals.push_back(const_string); + } + break; + + case 's': + show_scope = true; + break; + + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + } + + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + + name.clear(); + use_objc = false; + use_regex = false; + show_args = true; + show_locals = true; + show_globals = true; + show_types = true; + show_scope = false; + show_summary = true; + show_location = false; + debug = false; + max_depth = UINT32_MAX; + ptr_depth = 0; + globals.clear(); + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + std::string name; + bool use_objc; + bool use_regex; + bool show_args; + bool show_locals; + bool show_globals; + bool show_types; + bool show_scope; // local/arg/global/static + bool show_summary; + bool show_location; + bool debug; + uint32_t max_depth; // The depth to print when dumping concrete (not pointers) aggreate values + uint32_t ptr_depth; // The default depth that is dumped when we find pointers + std::vector globals; + // Instance variables to hold the values for command options. + }; + + CommandObjectVariableList () : + CommandObject ( + "variable list", + "Show specified argument, local variable, static variable or global variable. If none specified, list them all.", + "variable list [] [ [...]]") + { + } + + virtual + ~CommandObjectVariableList () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + void + DumpVariable (CommandReturnObject &result, ExecutionContext *exe_ctx, Variable *variable) + { + if (variable) + { + Stream &s = result.GetOutputStream(); + DWARFExpression &expr = variable->LocationExpression(); + Value expr_result; + Error expr_error; + Type *variable_type = variable->GetType(); + bool expr_success = expr.Evaluate(exe_ctx, NULL, NULL, expr_result, &expr_error); + + if (m_options.debug) + s.Printf ("Variable{0x%8.8x}: ", variable->GetID()); + + if (!expr_success) + s.Printf ("%s = ERROR (%s)", variable->GetName().AsCString(NULL), expr_error.AsCString()); + else + { + Value::ValueType expr_value_type = expr_result.GetValueType(); + switch (expr_value_type) + { + case Value::eValueTypeScalar: + s.Printf ("%s = ", variable->GetName().AsCString(NULL)); + if (variable_type) + { + DataExtractor data; + if (expr_result.ResolveValue (exe_ctx, NULL).GetData (data)) + variable_type->DumpValue (exe_ctx, &s, data, 0, m_options.show_types, m_options.show_summary, m_options.debug); + } + break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + { + s.Printf ("%s = ", variable->GetName().AsCString(NULL)); + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + lldb::AddressType addr_type = eAddressTypeLoad; + + if (expr_value_type == Value::eValueTypeFileAddress) + { + lldb::addr_t file_addr = expr_result.ResolveValue (exe_ctx, NULL).ULongLong(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()); + addr = so_addr.GetLoadAddress(exe_ctx->process); + } + if (addr == LLDB_INVALID_ADDRESS) + { + result.GetErrorStream().Printf ("error: %s is not loaded", var_sc.module_sp->GetFileSpec().GetFilename().AsCString()); + } + } + else + { + result.GetErrorStream().Printf ("error: unable to resolve the variable address 0x%llx", file_addr); + } + } + else + { + if (expr_value_type == Value::eValueTypeHostAddress) + addr_type = eAddressTypeHost; + addr = expr_result.ResolveValue (exe_ctx, NULL).ULongLong(LLDB_INVALID_ADDRESS); + } + + if (addr != LLDB_INVALID_ADDRESS) + { + if (m_options.debug) + s.Printf("@ 0x%8.8llx, value = ", addr); + variable_type->DumpValueInMemory (exe_ctx, &s, addr, addr_type, m_options.show_types, m_options.show_summary, m_options.debug); + } + } + break; + } + } + s.EOL(); + } + } + + void + DumpValueObject (CommandReturnObject &result, + ExecutionContextScope *exe_scope, + ValueObject *valobj, + const char *root_valobj_name, + uint32_t ptr_depth, + uint32_t curr_depth, + uint32_t max_depth, + bool use_objc) + { + if (valobj) + { + Stream &s = result.GetOutputStream(); + + //const char *loc_cstr = valobj->GetLocationAsCString(); + if (m_options.show_location) + { + s.Printf("@ %s: ", valobj->GetLocationAsCString(exe_scope)); + } + if (m_options.debug) + s.Printf ("%p ValueObject{%u} ", valobj, valobj->GetID()); + + s.Indent(); + + if (m_options.show_types) + s.Printf("(%s) ", valobj->GetTypeName().AsCString()); + + const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); + s.Printf ("%s = ", name_cstr); + + const char *val_cstr = valobj->GetValueAsCString(exe_scope); + const char *err_cstr = valobj->GetError().AsCString(); + + if (err_cstr) + { + s.Printf ("error: %s\n", err_cstr); + } + else + { + const char *sum_cstr = valobj->GetSummaryAsCString(exe_scope); + + const bool is_aggregate = ClangASTContext::IsAggregateType (valobj->GetOpaqueClangQualType()); + + if (val_cstr) + s.PutCString(val_cstr); + + if (sum_cstr) + s.Printf(" %s", sum_cstr); + + if (use_objc) + { + if (!ClangASTContext::IsPointerType (valobj->GetOpaqueClangQualType())) + return; + + if (!valobj->GetValueIsValid()) + return; + + Process *process = exe_scope->CalculateProcess(); + + if (!process) + return; + + Scalar scalar; + + if (!Type::GetValueAsScalar (valobj->GetClangAST(), + valobj->GetOpaqueClangQualType(), + valobj->GetDataExtractor(), + 0, + valobj->GetByteSize(), + scalar)) + return; + + ConstString po_output; + + ExecutionContext exe_ctx; + exe_scope->Calculate(exe_ctx); + + Value val(scalar); + val.SetContext(Value::eContextTypeOpaqueClangQualType, + ClangASTContext::GetVoidPtrType(valobj->GetClangAST(), false)); + + if (!process->GetObjCObjectPrinter().PrintObject(po_output, val, exe_ctx)) + return; + + s.Printf("\n%s\n", po_output.GetCString()); + + return; + } + + + if (curr_depth < max_depth) + { + if (is_aggregate) + s.PutChar('{'); + + bool is_ptr_or_ref = ClangASTContext::IsPointerOrReferenceType (valobj->GetOpaqueClangQualType()); + + if (is_ptr_or_ref && ptr_depth == 0) + return; + + const uint32_t num_children = valobj->GetNumChildren(); + if (num_children) + { + s.IndentMore(); + for (uint32_t idx=0; idxGetChildAtIndex(idx, true)); + if (child_sp.get()) + { + s.EOL(); + DumpValueObject (result, + exe_scope, + child_sp.get(), + NULL, + is_ptr_or_ref ? ptr_depth - 1 : ptr_depth, + curr_depth + 1, + max_depth, + false); + if (idx + 1 < num_children) + s.PutChar(','); + } + } + s.IndentLess(); + } + if (is_aggregate) + { + s.EOL(); + s.Indent("}"); + } + } + else + { + if (is_aggregate) + { + s.PutCString("{...}"); + } + } + + } + } + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.frame == NULL) + { + result.AppendError ("invalid frame"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + VariableList variable_list; + + SymbolContext frame_sc = exe_ctx.frame->GetSymbolContext (eSymbolContextEverything); + if (exe_ctx.frame && frame_sc.block) + frame_sc.block->AppendVariables(true, true, &variable_list); + VariableSP var_sp; + ValueObjectSP valobj_sp; + //ValueObjectList &valobj_list = exe_ctx.frame->GetValueObjectList(); + const char *name_cstr = NULL; + size_t idx; + if (!m_options.globals.empty()) + { + uint32_t fail_count = 0; + Target *target = context->GetTarget(); + if (target) + { + const size_t num_globals = m_options.globals.size(); + for (idx = 0; idx < num_globals; ++idx) + { + VariableList global_var_list; + const uint32_t num_matching_globals = target->GetImages().FindGlobalVariables (m_options.globals[idx], true, UINT32_MAX, global_var_list); + + if (num_matching_globals == 0) + { + ++fail_count; + result.GetErrorStream().Printf ("error: can't find global variable '%s'\n", m_options.globals[idx].AsCString()); + } + else + { + for (uint32_t global_idx=0; global_idxGetValueObjectList().FindValueObjectByValueName (m_options.globals[idx].AsCString()); + if (!valobj_sp) + valobj_sp.reset (new ValueObjectVariable (var_sp)); + + if (valobj_sp) + { + exe_ctx.frame->GetValueObjectList().Append (valobj_sp); + DumpValueObject (result, exe_ctx.frame, valobj_sp.get(), name_cstr, m_options.ptr_depth, 0, m_options.max_depth, false); + result.GetOutputStream().EOL(); + } + } + } + } + } + } + if (fail_count) + { + result.SetStatus (eReturnStatusFailed); + } + } + + if (command.GetArgumentCount() > 0) + { + // 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) + { + uint32_t ptr_depth = m_options.ptr_depth; + // If first character is a '*', then show pointer contents + if (name_cstr[0] == '*') + { + ++ptr_depth; + name_cstr++; // Skip the '*' + } + + std::string var_path (name_cstr); + size_t separator_idx = var_path.find_first_of(".-["); + + ConstString name_const_string; + if (separator_idx == std::string::npos) + name_const_string.SetCString (var_path.c_str()); + else + name_const_string.SetCStringWithLength (var_path.c_str(), separator_idx); + + var_sp = variable_list.FindVariable(name_const_string); + if (var_sp) + { + //DumpVariable (result, &exe_ctx, var_sp.get()); + // TODO: redo history variables using a different map +// if (var_path[0] == '$') +// valobj_sp = valobj_list.FindValueObjectByValueObjectName (name_const_string.GetCString()); +// else + valobj_sp = exe_ctx.frame->GetValueObjectList().FindValueObjectByValueName (name_const_string.GetCString()); + + if (!valobj_sp) + { + valobj_sp.reset (new ValueObjectVariable (var_sp)); + exe_ctx.frame->GetValueObjectList().Append (valobj_sp); + } + + var_path.erase (0, name_const_string.GetLength ()); + // We are dumping at least one child + while (separator_idx != std::string::npos) + { + // Calculate the next separator index ahead of time + ValueObjectSP child_valobj_sp; + const char separator_type = var_path[0]; + switch (separator_type) + { + + case '-': + if (var_path.size() >= 2 && var_path[1] != '>') + { + result.GetErrorStream().Printf ("error: invalid character in variable path starting at '%s'\n", + var_path.c_str()); + var_path.clear(); + valobj_sp.reset(); + break; + } + var_path.erase (0, 1); // Remove the '-' + // Fall through + case '.': + { + var_path.erase (0, 1); // Remove the '.' or '>' + separator_idx = var_path.find_first_of(".-["); + ConstString child_name; + if (separator_idx == std::string::npos) + child_name.SetCString (var_path.c_str()); + else + child_name.SetCStringWithLength(var_path.c_str(), separator_idx); + + child_valobj_sp = valobj_sp->GetChildMemberWithName (child_name, true); + if (!child_valobj_sp) + { + result.GetErrorStream().Printf ("error: can't find child of '%s' named '%s'\n", + valobj_sp->GetName().AsCString(), + child_name.GetCString()); + var_path.clear(); + valobj_sp.reset(); + break; + } + // Remove the child name from the path + var_path.erase(0, child_name.GetLength()); + } + break; + + case '[': + // Array member access, or treating pointer as an array + if (var_path.size() > 2) // Need at least two brackets and a number + { + char *end = NULL; + int32_t child_index = ::strtol (&var_path[1], &end, 0); + if (end && *end == ']') + { + + if (valobj_sp->IsPointerType ()) + { + child_valobj_sp = valobj_sp->GetSyntheticArrayMemberFromPointer (child_index, true); + } + else + { + child_valobj_sp = valobj_sp->GetChildAtIndex (child_index, true); + } + + if (!child_valobj_sp) + { + result.GetErrorStream().Printf ("error: invalid array index %u in '%s'\n", + child_index, + valobj_sp->GetName().AsCString()); + var_path.clear(); + valobj_sp.reset(); + break; + } + + // Erase the array member specification '[%i]' where %i is the array index + var_path.erase(0, (end - var_path.c_str()) + 1); + separator_idx = var_path.find_first_of(".-["); + + // Break out early from the switch since we were able to find the child member + break; + } + } + result.GetErrorStream().Printf ("error: invalid array member specification for '%s' starting at '%s'\n", + valobj_sp->GetName().AsCString(), + var_path.c_str()); + var_path.clear(); + valobj_sp.reset(); + break; + + break; + + default: + result.GetErrorStream().Printf ("error: invalid character in variable path starting at '%s'\n", + var_path.c_str()); + var_path.clear(); + valobj_sp.reset(); + separator_idx = std::string::npos; + break; + } + + if (child_valobj_sp) + valobj_sp = child_valobj_sp; + + if (var_path.empty()) + break; + + } + + if (valobj_sp) + { + DumpValueObject (result, exe_ctx.frame, valobj_sp.get(), name_cstr, ptr_depth, 0, m_options.max_depth, m_options.use_objc); + result.GetOutputStream().EOL(); + } + } + else + { + result.GetErrorStream().Printf ("error: unable to find any variables named '%s'\n", name_cstr); + var_path.clear(); + } + } + } + else + { + + if (m_options.show_globals) + { + if (frame_sc.comp_unit) + { + variable_list.AddVariables (frame_sc.comp_unit->GetVariableList(true).get()); + } + } + + const uint32_t num_variables = variable_list.GetSize(); + + if (num_variables > 0) + { + for (uint32_t i=0; iGetScope()) + { + case eValueTypeVariableGlobal: + dump_variable = m_options.show_globals; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString("GLOBAL: "); + break; + + case eValueTypeVariableStatic: + dump_variable = m_options.show_globals; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString("STATIC: "); + break; + + case eValueTypeVariableArgument: + dump_variable = m_options.show_args; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString(" ARG: "); + break; + + case eValueTypeVariableLocal: + dump_variable = m_options.show_locals; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString(" LOCAL: "); + break; + + default: + break; + } + + if (dump_variable) + DumpVariable (result, &exe_ctx, variable); + } + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + return result.Succeeded(); + } +protected: + + CommandOptions m_options; +}; + +lldb::OptionDefinition +CommandObjectVariableList::CommandOptions::g_option_table[] = +{ +{ 0, false, "debug", 'D', no_argument, NULL, 0, NULL, "Show verbose debug information."}, +{ 0, false, "depth", 'd', required_argument, NULL, 0, "", "Set the max recurse depth when dumping aggregate types (default is infinity)."}, +{ 0, false, "globals", 'g', no_argument, NULL, 0, NULL, "List global and static variables for the current stack frame source file."}, +{ 0, false, "global", 'G', required_argument, NULL, 0, NULL, "Find a global variable by name (which might not be in the current stack frame source file)."}, +{ 0, false, "location", 'L', no_argument, NULL, 0, NULL, "Show variable location information."}, +{ 0, false, "name", 'n', required_argument, NULL, 0, "", "Lookup a variable by name or regex (--regex) for the current execution context."}, +{ 0, false, "no-args", 'a', no_argument, NULL, 0, NULL, "Omit function arguments."}, +{ 0, false, "no-locals", 'l', no_argument, NULL, 0, NULL, "Omit local variables."}, +{ 0, false, "no-types", 't', no_argument, NULL, 0, NULL, "Omit variable type names."}, +{ 0, false, "no-summary", 'y', no_argument, NULL, 0, NULL, "Omit summary information."}, +{ 0, false, "scope", 's', no_argument, NULL, 0, NULL, "Show variable scope (argument, local, global, static)."}, +{ 0, false, "objc", 'o', no_argument, NULL, 0, NULL, "When looking up a variable by name (--name), print as an Objective-C object."}, +{ 0, false, "ptr-depth", 'p', required_argument, NULL, 0, "", "The number of pointers to be traversed when dumping values (default is zero)."}, +{ 0, false, "regex", 'r', no_argument, NULL, 0, NULL, "The argument for name lookups are regular expressions."}, +{ 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + +//---------------------------------------------------------------------- +// CommandObjectVariable constructor +//---------------------------------------------------------------------- +CommandObjectVariable::CommandObjectVariable(CommandInterpreter *interpreter) : + CommandObjectMultiword ("variable", + "Access program arguments, locals, static and global variables.", + "variable [list] ...") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectVariableList ()), "list", interpreter); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectVariable::~CommandObjectVariable() +{ +} + + + + diff --git a/lldb/source/Commands/CommandObjectVariable.h b/lldb/source/Commands/CommandObjectVariable.h new file mode 100644 index 000000000000..65869c73d724 --- /dev/null +++ b/lldb/source/Commands/CommandObjectVariable.h @@ -0,0 +1,43 @@ +//===-- CommandObjectVariable.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectVariable_h_ +#define liblldb_CommandObjectVariable_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectImage +//------------------------------------------------------------------------- + +class CommandObjectVariable : public CommandObjectMultiword +{ +public: + + CommandObjectVariable (CommandInterpreter *iterpreter); + + virtual + ~CommandObjectVariable (); + +private: + //------------------------------------------------------------------ + // For CommandObjectVariable only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectVariable); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectVariable_h_ diff --git a/lldb/source/Core/Address.cpp b/lldb/source/Core/Address.cpp new file mode 100644 index 000000000000..e2481e133fa4 --- /dev/null +++ b/lldb/source/Core/Address.cpp @@ -0,0 +1,875 @@ +//===-- 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/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.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; + + lldb::AddressType addr_type = eAddressTypeInvalid; + addr_t addr = LLDB_INVALID_ADDRESS; + + Process *process = exe_scope->CalculateProcess(); + + if (process && process->IsAlive()) + { + addr = address.GetLoadAddress(process); + if (addr != LLDB_INVALID_ADDRESS) + addr_type = eAddressTypeLoad; + } + + if (addr == LLDB_INVALID_ADDRESS) + { + addr = address.GetFileAddress(); + if (addr != LLDB_INVALID_ADDRESS) + addr_type = eAddressTypeFile; + } + + if (addr_type == eAddressTypeInvalid) + return false; + + Target *target = exe_scope->CalculateTarget(); + if (target) + { + Error error; + return target->ReadMemory (addr_type, addr, dst, dst_len, error, NULL); + } + 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; + + Process *process = exe_scope->CalculateProcess(); + if (process) + { + byte_order = process->GetByteOrder(); + addr_size = process->GetAddressByteSize(); + } + + if (byte_order == eByteOrderInvalid || addr_size == 0) + { + Module *module = address.GetModule(); + if (module) + { + byte_order = module->GetArchitecture().GetDefaultEndian(); + addr_size = module->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; + + 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); + uint32_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) + { + Process *process = exe_scope->CalculateProcess(); + if (process && process->IsAlive()) + { + if (!process->ResolveLoadAddress (deref_addr, deref_so_addr)) + { + deref_so_addr.SetSection(NULL); + deref_so_addr.SetOffset(deref_addr); + } + } + else + { + Target *target = exe_scope->CalculateTarget(); + if (target == NULL) + return false; + + if (!target->GetImages().ResolveFileAddress(deref_addr, deref_so_addr)) + { + deref_so_addr.SetSection(NULL); + deref_so_addr.SetOffset(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) + 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.data(), 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 adderss size don't matter for C string dumping.. + DataExtractor data (buf, sizeof(buf), eByteOrderHost, 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 () : + m_section (NULL), + m_offset (LLDB_INVALID_ADDRESS) +{ +} + +Address::Address (const Address& rhs) : + m_section (rhs.m_section), + m_offset (rhs.m_offset) +{ +} + +Address::Address (const Section* section, addr_t offset) : + m_section (section), + m_offset (offset) +{ +} + +Address::Address (addr_t address, const SectionList * sections) : + m_section (NULL), + m_offset (LLDB_INVALID_ADDRESS) +{ + ResolveAddressUsingFileSections(address, sections); +} + +Address::~Address () +{ +} + + +const Address& +Address::operator= (const Address& rhs) +{ + if (this != &rhs) + { + m_section = rhs.m_section; + m_offset = rhs.m_offset; + } + return *this; +} + +bool +Address::IsValid() const +{ + return m_offset != LLDB_INVALID_ADDRESS; +} + +bool +Address::IsSectionOffset() const +{ + return m_section != NULL && IsValid(); +} + +bool +Address::ResolveAddressUsingFileSections (addr_t addr, const SectionList *sections) +{ + if (sections) + m_section = sections->FindSectionContainingFileAddress(addr).get(); + else + m_section = NULL; + + if (m_section != NULL) + { + assert( m_section->ContainsFileAddress(addr) ); + m_offset = addr - m_section->GetFileAddress(); + return true; // Successfully transformed addr into a section offset address + } + + m_offset = addr; + return false; // Failed to resolve this address to a section offset value +} + +//bool +//Address::ResolveAddressUsingLoadSections (addr_t addr, const SectionList *sections) +//{ +// if (sections) +// m_section = sections->FindSectionContainingLoadAddress(addr).get(); +// else +// m_section = NULL; +// +// if (m_section != NULL) +// { +// assert( m_section->ContainsLoadAddress(addr) ); +// m_offset = addr - m_section->GetLoadBaseAddress(); +// return true; // Successfully transformed addr into a section offset address +// } +// +// m_offset = addr; +// return false; // Failed to resolve this address to a section offset value +//} +// +Module * +Address::GetModule () const +{ + if (m_section) + return m_section->GetModule(); + return NULL; +} + +const Section* +Address::GetSection () const +{ + return m_section; +} + + +//addr_t +//Address::Address() const +//{ +// addr_t addr = GetLoadAddress(); +// if (addr != LLDB_INVALID_ADDRESS) +// return addr; +// return GetFileAddress(); +//} +// + +addr_t +Address::GetFileAddress () const +{ + if (m_section != NULL) + { + addr_t sect_file_addr = m_section->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 (Process *process) const +{ + if (m_section != NULL) + { + if (process) + { + addr_t sect_load_addr = m_section->GetLoadBaseAddress (process); + + 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; + } + // No section, we just return the offset since it is the value in this case + return m_offset; +} + +addr_t +Address::GetOffset () const +{ + return m_offset; +} + +bool +Address::SetOffset (addr_t offset) +{ + bool changed = m_offset != offset; + m_offset = offset; + return changed; +} + +void +Address::SetSection (const Section* section) +{ + m_section = section; +} + +void +Address::Clear() +{ + m_section = NULL; + m_offset = LLDB_INVALID_ADDRESS; +} + + +bool +Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style) const +{ + // If the section was NULL, only load address is going to work. + if (m_section == NULL) + style = DumpStyleLoadAddress; + + Process *process = NULL; + if (exe_scope) + process = exe_scope->CalculateProcess(); + int addr_size = sizeof (addr_t); + if (process) + addr_size = process->GetAddressByteSize (); + + lldb_private::Address so_addr; + + switch (style) + { + case DumpStyleSectionNameOffset: + if (m_section != NULL) + { + m_section->DumpName(s); + s->Printf (" + %llu", m_offset); + } + else + { + s->Printf("0x%16.16llx", m_offset); + } + break; + + case DumpStyleSectionPointerOffset: + s->Printf("(Section *)%.*p + 0x%16.16llx", (int)sizeof(void*) * 2, m_section, m_offset); + break; + + case DumpStyleModuleWithFileAddress: + s->Printf("%s[", m_section->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); + return false; + } + s->Address (file_addr, addr_size); + if (style == DumpStyleModuleWithFileAddress) + s->PutChar(']'); + } + break; + + case DumpStyleLoadAddress: + { + addr_t load_addr = GetLoadAddress (process); + if (load_addr == LLDB_INVALID_ADDRESS) + { + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style); + return false; + } + s->Address (load_addr, addr_size); + } + break; + + case DumpStyleResolvedDescription: + if (IsSectionOffset()) + { + lldb::AddressType addr_type = eAddressTypeLoad; + addr_t addr = GetLoadAddress (process); + if (addr == LLDB_INVALID_ADDRESS) + { + addr = GetFileAddress(); + addr_type = eAddressTypeFile; + } + + uint32_t pointer_size = 4; + lldb_private::Module *module = GetModule(); + if (process) + pointer_size = process->GetAddressByteSize(); + else if (module) + pointer_size = module->GetArchitecture().GetAddressByteSize(); + + bool showed_info = false; + const Section *section = GetSection(); + if (section) + { + SectionType sect_type = section->GetSectionType(); + switch (sect_type) + { + 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 (so_addr.IsSectionOffset()) + { + lldb_private::SymbolContext func_sc; + process->GetTarget().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, process, so_addr, true); + if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("), (char *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(" ("); +#else + s->PutCString(", "); +#endif + ReadCStringFromMemory (exe_scope, so_addr, s); + } +#if VERBOSE_OUTPUT + s->PutCString(") }"); +#else + s->PutCString(" }"); +#endif + } + } + } + } + break; + + case eSectionTypeDataObjCCFStrings: + { + Address cfstring_data_addr(*this); + cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() + (2 * pointer_size)); + if (ReadAddress (exe_scope, cfstring_data_addr, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("(CFString *) "); + cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(" -> @"); +#else + s->PutChar('@'); +#endif + if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription)) + showed_info = true; + } + } + break; + + case eSectionTypeData4: + // Read the 4 byte data and display it + showed_info = true; + s->PutCString("(uint32_t) "); + DumpUInt (exe_scope, *this, 4, s); + break; + + case eSectionTypeData8: + // Read the 8 byte data and display it + showed_info = true; + s->PutCString("(uint64_t) "); + DumpUInt (exe_scope, *this, 8, s); + break; + + case eSectionTypeData16: + // Read the 16 byte data and display it + showed_info = true; + s->PutCString("(uint128_t) "); + DumpUInt (exe_scope, *this, 16, s); + break; + + case eSectionTypeDataPointers: + // Read the pointer data and display it + { + if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) + { + s->PutCString ("(void *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + + showed_info = true; + if (so_addr.IsSectionOffset()) + { + lldb_private::SymbolContext pointer_sc; + process->GetTarget().GetImages().ResolveSymbolContextForAddress (so_addr, + eSymbolContextEverything, + pointer_sc); + if (pointer_sc.function || pointer_sc.symbol) + { + s->PutCString(": "); + pointer_sc.DumpStopContext(s, process, so_addr, false); + } + } + } + } + break; + } + } + + if (!showed_info) + { + if (module) + { + lldb_private::SymbolContext sc; + module->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); + if (sc.function || sc.symbol) + { + bool show_stop_context = 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->GetAddressRangePtr()) + { + if (sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetSection() != GetSection()) + 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, process, *this, false); + } + 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); + return false; + } + break; + } + + return true; +} + +//Stream& operator << (Stream& s, const Address& so_addr) +//{ +// so_addr.Dump(&s, Address::DumpStyleSectionNameOffset); +// return s; +//} +// +void +Address::CalculateSymbolContext (SymbolContext *sc) +{ + sc->Clear(); + // Absolute addresses don't have enough information to reconstruct even their target. + if (m_section == NULL) + return; + + if (m_section->GetModule()) + { + sc->module_sp = m_section->GetModule()->GetSP(); + if (sc->module_sp) + sc->module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextEverything, *sc); + } +} + +void +Address::DumpSymbolContext (Stream *s) +{ + SymbolContext sc; + CalculateSymbolContext (&sc); + sc.Dump (s, NULL); +} + +void +Address::DumpDebug(Stream *s) const +{ + *s << (void *)this << ": " << "Address"; + if (m_section != NULL) + { + *s << ", section = " << (void *)m_section << " (" << m_section->GetName() << "), offset = " << m_offset; + } + else + { + *s << ", vm_addr = " << m_offset; + } + s->EOL(); +} + +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, Process *process) +{ + assert (process != NULL); + addr_t a_load_addr = a.GetLoadAddress (process); + addr_t b_load_addr = b.GetLoadAddress (process); + 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) +{ + Module *a_module = a.GetModule (); + Module *b_module = b.GetModule (); + 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); +} + + +/// The only comparisons that make sense are the load addresses +//bool +//lldb::operator< (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr < rhs_addr; +//} +// +//bool +//lldb::operator<= (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr <= rhs_addr; +//} +// +//bool +//lldb::operator> (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr > rhs_addr; +//} +// +//bool +//lldb::operator>= (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr >= rhs_addr; +//} +// + +// The operator == checks for exact equality only (same section, same offset) +bool +lldb_private::operator== (const Address& a, const Address& rhs) +{ + return a.GetSection() == rhs.GetSection() && + a.GetOffset() == rhs.GetOffset(); +} +// The operator != checks for exact inequality only (differing section, or +// different offset) +bool +lldb_private::operator!= (const Address& a, const Address& rhs) +{ + return a.GetSection() != rhs.GetSection() || + a.GetOffset() != rhs.GetOffset(); +} + +bool +Address::IsLinkedAddress () const +{ + return m_section && m_section->GetLinkedSection(); +} + + +void +Address::ResolveLinkedAddress () +{ + if (m_section) + { + const Section *linked_section = m_section->GetLinkedSection(); + if (linked_section) + { + m_offset += m_section->GetLinkedOffset(); + m_section = linked_section; + } + } +} diff --git a/lldb/source/Core/AddressRange.cpp b/lldb/source/Core/AddressRange.cpp new file mode 100644 index 000000000000..8c2997936fd7 --- /dev/null +++ b/lldb/source/Core/AddressRange.cpp @@ -0,0 +1,222 @@ +//===-- 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/Stream.h" +#include "lldb/Target/Process.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 Section* section, addr_t offset, addr_t byte_size) : + m_base_addr(section, offset), + m_byte_size(byte_size) +{ +} + +AddressRange::AddressRange (const Address& so_addr, addr_t byte_size) : + m_base_addr(so_addr), + m_byte_size(byte_size) +{ +} + +AddressRange::~AddressRange () +{ +} + +Address & +AddressRange::GetBaseAddress() +{ + return m_base_addr; +} + +const Address & +AddressRange::GetBaseAddress() const +{ + return m_base_addr; +} + +addr_t +AddressRange::GetByteSize() const +{ + return m_byte_size; +} + +void +AddressRange::SetByteSize(addr_t byte_size) +{ + m_byte_size = byte_size; +} + +//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, Process *process) const +{ + if (addr.GetSection() == m_base_addr.GetSection()) + return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize(); + addr_t load_base_addr = GetBaseAddress().GetLoadAddress(process); + if (load_base_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t load_addr = addr.GetLoadAddress(process); + 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, Process *process) const +{ + if (load_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t load_base_addr = GetBaseAddress().GetLoadAddress(process); + 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, Process *process, Address::DumpStyle style, Address::DumpStyle fallback_style) const +{ + addr_t vmaddr = LLDB_INVALID_ADDRESS; + int addr_size = sizeof (addr_t); + if (process) + addr_size = process->GetAddressByteSize (); + + switch (style) + { + case Address::DumpStyleSectionNameOffset: + case Address::DumpStyleSectionPointerOffset: + s->PutChar ('['); + m_base_addr.Dump(s, process, style, fallback_style); + s->PutChar ('-'); + s->Address (m_base_addr.GetOffset() + GetByteSize(), addr_size); + s->PutChar (')'); + return true; + break; + + case Address::DumpStyleFileAddress: + vmaddr = m_base_addr.GetFileAddress(); + break; + + case Address::DumpStyleLoadAddress: + vmaddr = m_base_addr.GetLoadAddress(process); + break; + } + + if (vmaddr != LLDB_INVALID_ADDRESS) + { + s->AddressRange(vmaddr, vmaddr + GetByteSize(), addr_size); + return true; + } + + return false; +} + + +void +AddressRange::DumpDebug (Stream *s) const +{ + s->Printf("%.*p: AddressRange section = %*p, offset = 0x%16.16llx, byte_size = 0x%16.16llx\n", (int)sizeof(void*) * 2, this, (int)sizeof(void*) * 2, m_base_addr.GetSection(), m_base_addr.GetOffset(), GetByteSize()); +} + +size_t +AddressRange::MemorySize () const +{ + // Noting special for the memory size of a single AddressRange object, + // it is just the size of itself. + return sizeof(AddressRange); +} +// +//bool +//lldb::operator== (const AddressRange& lhs, const AddressRange& rhs) +//{ +// if (lhs.GetBaseAddress() == rhs.GetBaseAddress()) +// return lhs.GetByteSize() == rhs.GetByteSize(); +// return false; +//} diff --git a/lldb/source/Core/AddressResolver.cpp b/lldb/source/Core/AddressResolver.cpp new file mode 100644 index 000000000000..5369d960f251 --- /dev/null +++ b/lldb/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/lldb/source/Core/AddressResolverFileLine.cpp b/lldb/source/Core/AddressResolverFileLine.cpp new file mode 100644 index 000000000000..c4aadcca9c1c --- /dev/null +++ b/lldb/source/Core/AddressResolverFileLine.cpp @@ -0,0 +1,100 @@ +//===-- 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/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 (int 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%llx 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/lldb/source/Core/AddressResolverName.cpp b/lldb/source/Core/AddressResolverName.cpp new file mode 100644 index 000000000000..9e154c6e7039 --- /dev/null +++ b/lldb/source/Core/AddressResolverName.cpp @@ -0,0 +1,231 @@ +//===-- 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/StreamString.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; + } + + 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, false, 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, true, 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->GetAddressRangePtr()) + { + if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddressRangePtr()->GetBaseAddress()) + { + 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->GetAddressRangePtr()) + { + func_addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress(); + addr_t byte_size = sc.symbol->GetAddressRangePtr()->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/lldb/source/Core/ArchSpec.cpp b/lldb/source/Core/ArchSpec.cpp new file mode 100644 index 000000000000..3cc8232ac970 --- /dev/null +++ b/lldb/source/Core/ArchSpec.cpp @@ -0,0 +1,1681 @@ +//===-- 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 + +using namespace lldb; +using namespace lldb_private; + +#define ARCH_SPEC_SEPARATOR_CHAR '-' + +#ifndef CPU_TYPE_ARM +#define CPU_TYPE_ARM ((cpu_type_t) 12) +#endif + +#ifndef CPU_SUBTYPE_ARM_ALL +#define CPU_SUBTYPE_ARM_ALL ((cpu_subtype_t) 0) +#endif + +#ifndef CPU_SUBTYPE_ARM_V4T +#define CPU_SUBTYPE_ARM_V4T ((cpu_subtype_t) 5) +#endif + +#ifndef CPU_SUBTYPE_ARM_V6 +#define CPU_SUBTYPE_ARM_V6 ((cpu_subtype_t) 6) +#endif + +#ifndef CPU_SUBTYPE_ARM_V5TEJ +#define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif + +#ifndef CPU_SUBTYPE_ARM_XSCALE +#define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7 +#define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif + +//---------------------------------------------------------------------- +// A structure that describes all of the information we want to know +// about each architecture. +//---------------------------------------------------------------------- +struct ArchDefinition +{ + uint32_t cpu; + uint32_t sub; + const char *name; +}; + +//---------------------------------------------------------------------- +// 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 ArchDefinition g_arch_defs[] = +{ + { CPU_TYPE_ANY, CPU_TYPE_ANY , "all" }, + { CPU_TYPE_ARM, CPU_TYPE_ANY , "arm" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_ALL , "arm" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T , "armv4" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ , "armv5" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 , "armv6" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 , "armv7" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_XSCALE , "xscale" }, + { CPU_TYPE_POWERPC, CPU_TYPE_ANY , "ppc" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL , "ppc" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_601 , "ppc601" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_602 , "ppc602" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603 , "ppc603" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603e , "ppc603e" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603ev , "ppc603ev" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604 , "ppc604" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604e , "ppc604e" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_620 , "ppc620" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750 , "ppc750" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 , "ppc7400" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7450 , "ppc7450" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_970 , "ppc970" }, + { CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL , "ppc64" }, + { CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_970 , "ppc970-64" }, + { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL , "i386" }, + { CPU_TYPE_I386, CPU_SUBTYPE_486 , "i486" }, + { CPU_TYPE_I386, CPU_SUBTYPE_486SX , "i486sx" }, + { CPU_TYPE_I386, CPU_TYPE_ANY , "i386" }, + { CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL , "x86_64" }, + { CPU_TYPE_X86_64, CPU_TYPE_ANY , "x86_64" }, + + // TODO: when we get a platform that knows more about the host OS we should + // let it call some accessor funcitons to set the default system arch for + // the default, 32 and 64 bit cases instead of hard coding it in this + // table. + +#if defined (__i386__) || defined(__x86_64__) + { CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL , LLDB_ARCH_DEFAULT }, + { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL , LLDB_ARCH_DEFAULT_32BIT }, + { CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL , LLDB_ARCH_DEFAULT_64BIT }, +#elif defined (__arm__) + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 , LLDB_ARCH_DEFAULT }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 , LLDB_ARCH_DEFAULT_32BIT }, +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 , LLDB_ARCH_DEFAULT }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 , LLDB_ARCH_DEFAULT_32BIT }, + { CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_970 , LLDB_ARCH_DEFAULT_64BIT }, +#endif +}; + +//---------------------------------------------------------------------- +// Figure out how many architecture definitions we have +//---------------------------------------------------------------------- +const size_t k_num_arch_defs = sizeof(g_arch_defs)/sizeof(ArchDefinition); + + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +ArchSpec::ArchSpec() : + m_cpu (LLDB_INVALID_CPUTYPE), + m_sub (0) +{ +} + +//---------------------------------------------------------------------- +// Constructor that initializes the object with supplied cpu and +// subtypes. +//---------------------------------------------------------------------- +ArchSpec::ArchSpec(uint32_t cpu, uint32_t sub) : + m_cpu (cpu), + m_sub (sub) +{ +} + +//---------------------------------------------------------------------- +// Constructor that initializes the object with supplied +// architecture name. There are also predefined values in +// Defines.h: +// liblldb_ARCH_DEFAULT +// The arch the current system defaults to when a program is +// launched without any extra attributes or settings. +// +// liblldb_ARCH_DEFAULT_32BIT +// The 32 bit arch the current system defaults to (if any) +// +// liblldb_ARCH_DEFAULT_32BIT +// The 64 bit arch the current system defaults to (if any) +//---------------------------------------------------------------------- +ArchSpec::ArchSpec(const char *arch_name) : + m_cpu (LLDB_INVALID_CPUTYPE), + m_sub (0) +{ + if (arch_name) + SetArch(arch_name); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ArchSpec::~ArchSpec() +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const ArchSpec& +ArchSpec::operator= (const ArchSpec& rhs) +{ + if (this != &rhs) + { + m_cpu = rhs.m_cpu; + m_sub = rhs.m_sub; + } + return *this; +} + +//---------------------------------------------------------------------- +// Get a C string representation of the current architecture +//---------------------------------------------------------------------- +const char * +ArchSpec::AsCString() const +{ + return ArchSpec::AsCString(m_cpu, m_sub); +} + +//---------------------------------------------------------------------- +// Class function to get a C string representation given a CPU type +// and subtype. +//---------------------------------------------------------------------- +const char * +ArchSpec::AsCString(uint32_t cpu, uint32_t sub) +{ + for (uint32_t i=0; i +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +namespace lldb_private { + +class Tokenizer +{ +public: + Tokenizer (const char *separator_chars = NULL) : + m_tokenizer(NULL) + { + m_tokenizer = ::tok_init (separator_chars); + } + + ~Tokenizer () + { + if (m_tokenizer) + { + ::tok_end (m_tokenizer); + m_tokenizer = NULL; + } + } + + void + Reset () + { + assert (m_tokenizer); + ::tok_reset (m_tokenizer); + } + + int + TokenizeLineInfo (const ::LineInfo *line_info) + { + assert (m_tokenizer); + return ::tok_line (m_tokenizer, + line_info, + &m_argc, + &m_argv, + &m_cursor_arg_index, + &m_cursor_arg_offset); + } + + int + TokenizeCString (const char *cstr) + { + assert (m_tokenizer); + m_cursor_arg_index = -1; + m_cursor_arg_offset = -1; + return ::tok_str (m_tokenizer, + cstr, + &m_argc, + &m_argv); + } + + + int + GetArgCount () const + { + return m_argc; + } + + const char ** + GetArgVector () const + { + return m_argv; + } + + int + GetCursoreArgIndex () const + { + return m_cursor_arg_index; + } + + int + GetCursoreArgOffset () const + { + return m_cursor_arg_offset; + } + + +protected: + struct tokenizer* m_tokenizer; + const char **m_argv; + int m_argc; + int m_cursor_arg_index; + int m_cursor_arg_offset; +}; + +} // namespace lldb_private + +using namespace lldb; +using namespace lldb_private; + +static const char *k_space_characters = "\t\n\v\f\r "; +static const char *k_space_characters_with_slash = "\t\n\v\f\r \\"; + + +//---------------------------------------------------------------------- +// Args constructor +//---------------------------------------------------------------------- +Args::Args (const char *command) : + m_args(), + m_argv() +{ + SetCommandString (command); +} + + +Args::Args (const char *command, size_t len) : + m_args(), + m_argv() +{ + SetCommandString (command, len); +} + + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Args::~Args () +{ +} + +void +Args::Dump (Stream *s) +{ +// int argc = GetArgumentCount(); +// +// arg_sstr_collection::const_iterator pos, begin = m_args.begin(), end = m_args.end(); +// for (pos = m_args.begin(); pos != end; ++pos) +// { +// s->Indent(); +// s->Printf("args[%zu]=%s\n", std::distance(begin, pos), pos->c_str()); +// } +// s->EOL(); + const int argc = m_argv.size(); + for (int i=0; iIndent(); + const char *arg_cstr = m_argv[i]; + if (arg_cstr) + s->Printf("argv[%i]=\"%s\"\n", i, arg_cstr); + else + s->Printf("argv[%i]=NULL\n", i); + } + s->EOL(); +} + +bool +Args::GetCommandString (std::string &command) +{ + command.clear(); + int argc = GetArgumentCount(); + for (int i=0; i 0) + command += ' '; + command += 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(); + if (command && command[0]) + { + const char *arg_start; + const char *next_arg_start; + for (arg_start = command, next_arg_start = NULL; + arg_start && arg_start[0]; + arg_start = next_arg_start, next_arg_start = NULL) + { + // Skip any leading space characters + arg_start = ::strspn (arg_start, k_space_characters) + arg_start; + + // If there were only space characters to the end of the line, then + // we're done. + if (*arg_start == '\0') + break; + + std::string arg; + const char *arg_end = NULL; + + switch (*arg_start) + { + case '\'': + case '"': + case '`': + { + // Look for either a quote character, or the backslash + // character + const char quote_char = *arg_start; + char find_chars[3] = { quote_char, '\\' , '\0'}; + bool is_backtick = (quote_char == '`'); + if (quote_char == '"' || quote_char == '`') + m_args_quote_char.push_back(quote_char); + else + m_args_quote_char.push_back('\0'); + + while (*arg_start != '\0') + { + arg_end = ::strcspn (arg_start + 1, find_chars) + arg_start + 1; + + if (*arg_end == '\0') + { + arg.append (arg_start); + break; + } + + // Watch out for quote characters prefixed with '\' + if (*arg_end == '\\') + { + if (arg_end[1] == quote_char) + { + // The character following the '\' is our quote + // character so strip the backslash character + arg.append (arg_start, arg_end); + } + else + { + // The character following the '\' is NOT our + // quote character, so include the backslash + // and continue + arg.append (arg_start, arg_end + 1); + } + arg_start = arg_end + 1; + continue; + } + else + { + arg.append (arg_start, arg_end + 1); + next_arg_start = arg_end + 1; + break; + } + } + + // Skip single and double quotes, but leave backtick quotes + if (!is_backtick) + { + char first_c = arg[0]; + arg.erase(0,1); + // Only erase the last character if it is the same as the first. + // Otherwise, we're parsing an incomplete command line, and we + // would be stripping off the last character of that string. + if (arg[arg.size() - 1] == first_c) + arg.erase(arg.size() - 1, 1); + } + } + break; + default: + { + m_args_quote_char.push_back('\0'); + // Look for the next non-escaped space character + while (*arg_start != '\0') + { + arg_end = ::strcspn (arg_start, k_space_characters_with_slash) + arg_start; + + if (arg_end == NULL) + { + arg.append(arg_start); + break; + } + + if (*arg_end == '\\') + { + // Append up to the '\' char + arg.append (arg_start, arg_end); + + if (arg_end[1] == '\0') + break; + + // Append the character following the '\' if it isn't + // the end of the string + arg.append (1, arg_end[1]); + arg_start = arg_end + 2; + continue; + } + else + { + arg.append (arg_start, arg_end); + next_arg_start = arg_end; + break; + } + } + } + break; + } + + m_args.push_back(arg); + } + } + 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); +} + +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(); + 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); + + + 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(); + 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); + m_args_quote_char.erase(m_args_quote_char.begin() + idx); + } +} + +void +Args::SetArguments (int 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(); + + // Make a copy of the arguments in our internal buffer + size_t i; + // First copy each string + for (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'.\n", 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; + int32_t uval = ::strtol (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; +} + +uint32_t +Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + uint32_t uval = ::strtoul (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; +} + + +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 char *s, lldb::addr_t fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + lldb::addr_t addr = ::strtoull (s, &end, 0); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return addr; // All characters were used, return the result + } + // Try base 16 with no prefix... + addr = ::strtoull (s, &end, 16); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return addr; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +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; +} + +int32_t +Args::StringToOptionEnum (const char *s, lldb::OptionEnumValueElement *enum_values, int32_t fail_value, bool *success_ptr) +{ + if (enum_values && 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) + { + if (success_ptr) *success_ptr = true; + return enum_values[i].value; + } + } + } + if (success_ptr) *success_ptr = false; + + 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 +) +{ + format = eFormatInvalid; + Error error; + + if (s && s[0]) + { + switch (s[0]) + { + case 'y': format = eFormatBytes; break; + case 'Y': format = eFormatBytesWithASCII; break; + case 'b': format = eFormatBinary; break; + case 'B': format = eFormatBoolean; break; + case 'c': format = eFormatChar; break; + case 'C': format = eFormatCharPrintable; break; + case 'o': format = eFormatOctal; break; + case 'i': + case 'd': format = eFormatDecimal; break; + case 'u': format = eFormatUnsigned; break; + case 'x': format = eFormatHex; break; + case 'f': + case 'e': + case 'g': format = eFormatFloat; break; + case 'p': format = eFormatPointer; break; + case 's': format = eFormatCString; break; + default: + error.SetErrorStringWithFormat("Invalid format character '%c'. Valid values are:\n" + " b - binary\n" + " B - boolean\n" + " c - char\n" + " C - printable char\n" + " d - signed decimal\n" + " e - float\n" + " f - float\n" + " g - float\n" + " i - signed decimal\n" + " o - octal\n" + " s - c-string\n" + " u - unsigned decimal\n" + " x - hex\n" + " y - bytes\n" + " Y - bytes with ASCII\n", s[0]); + break; + } + + if (error.Fail()) + return error; + } + else + { + error.SetErrorStringWithFormat("%s option string.\n", s ? "empty" : "invalid"); + } + return error; +} + +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) + { + int 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 (int 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; + } +} + +void +Args::ParseAliasOptions +( + Options &options, + CommandReturnObject &result, + OptionArgVector *option_arg_vector +) +{ + 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; + } + } + } + + optreset = 1; + optind = 1; + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long (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", (char) val); + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), "")); + break; + case required_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + 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()), + std::string (optarg))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + "")); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + break; + default: + result.AppendErrorWithFormat + ("error with options table; invalid value in has_arg field for option '%c'.\n", + (char) val); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", (char) val); + result.SetStatus (eReturnStatusFailed); + } + if (!result.Succeeded()) + break; + } +} + +void +Args::ParseArgsForCompletion +( + Options &options, + OptionElementVector &option_element_vector +) +{ + StreamString sstr; + int i; + 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 (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; + } + } + } + + optreset = 1; + optind = 1; + opterr = 0; + + int val; + const OptionDefinition *opt_defs = options.GetDefinitions(); + + // Fooey... getopt_long permutes the GetArgumentVector for no apparent reason. + // So we have to build another Arg and pass that to getopt_long so it doesn't + // screw up the one we have. + + std::vector dummy_vec(GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); + + while (1) + { + bool missing_argument = false; + int parse_start = optind; + int long_options_index = -1; + val = ::getopt_long (dummy_vec.size() - 1,(char *const *) dummy_vec.data(), sstr.GetData(), long_options, + &long_options_index); + + if (val == -1) + break; + + else if (val == '?') + { + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + 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, parse_start, 0)); + break; + case required_argument: + if (optarg != NULL) + { + int arg_index; + if (missing_argument) + arg_index = -1; + else + arg_index = parse_start + 1; + + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, arg_index)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, -1)); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, parse_start + 1)); + } + break; + default: + // The options table is messed up. Here we'll just continue + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + break; + } + } + else + { + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + } + } +} diff --git a/lldb/source/Core/Baton.cpp b/lldb/source/Core/Baton.cpp new file mode 100644 index 000000000000..103e2e0ef0c4 --- /dev/null +++ b/lldb/source/Core/Baton.cpp @@ -0,0 +1,25 @@ +//===-- 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 +{ + s->Printf("baton: %p", m_data); +} diff --git a/lldb/source/Core/Broadcaster.cpp b/lldb/source/Core/Broadcaster.cpp new file mode 100644 index 000000000000..b311a8b3eede --- /dev/null +++ b/lldb/source/Core/Broadcaster.cpp @@ -0,0 +1,219 @@ +//===-- 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 (const char *name) : + m_broadcaster_name (name), + m_broadcaster_listeners (), + m_broadcaster_listeners_mutex (Mutex::eMutexTypeRecursive) +{ + 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()); + + // Scope for "listeners_locker" + { + Mutex::Locker listeners_locker(m_broadcaster_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_broadcaster_listeners.end(); + for (pos = m_broadcaster_listeners.begin(); pos != end; ++pos) + pos->first->BroadcasterWillDestruct (this); + + m_broadcaster_listeners.clear(); + } +} + +const ConstString & +Broadcaster::GetBroadcasterName () +{ + return m_broadcaster_name; +} + +void +Broadcaster::AddInitialEventsToListener (Listener *listener, uint32_t requested_events) +{ + +} + +uint32_t +Broadcaster::AddListener (Listener* listener, uint32_t event_mask) +{ + Mutex::Locker locker(m_broadcaster_listeners_mutex); + collection::iterator pos, end = m_broadcaster_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_broadcaster_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_broadcaster_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_broadcaster_listeners_mutex); + if (m_broadcaster_listeners.empty()) + return false; + + collection::iterator pos, end = m_broadcaster_listeners.end(); + for (pos = m_broadcaster_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_broadcaster_listeners_mutex); + collection::iterator pos, end = m_broadcaster_listeners.end(); + // See if we already have this listener, and if so, update its mask + for (pos = m_broadcaster_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_broadcaster_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(); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + { + StreamString event_description; + event_sp->Dump (&event_description); + log->Printf ("%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, unique =%i)", + this, + m_broadcaster_name.AsCString(""), + event_description.GetData(), + unique); + } + + Mutex::Locker event_types_locker(m_broadcaster_listeners_mutex); + collection::iterator pos, end = m_broadcaster_listeners.end(); + + + // Iterate through all listener/mask pairs + for (pos = m_broadcaster_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); +} + diff --git a/lldb/source/Core/Communication.cpp b/lldb/source/Core/Communication.cpp new file mode 100644 index 000000000000..3ec3e0f41d9b --- /dev/null +++ b/lldb/source/Core/Communication.cpp @@ -0,0 +1,363 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +Communication::Communication(const char *name) : + Broadcaster (name), + m_connection_ap (), + m_read_thread (NULL), + m_read_thread_enabled (false), + m_bytes(), + m_bytes_mutex (Mutex::eMutexTypeRecursive), + m_callback (NULL), + m_callback_baton (NULL) + +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Communication (name = %s)", + this, name); +} + +//---------------------------------------------------------------------- +// 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() +{ + StopReadThread (NULL); + Disconnect (NULL); +} + +ConnectionStatus +Communication::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + + if (m_connection_ap.get()) + return m_connection_ap->BytesAvailable (timeout_usec, error_ptr); + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +Communication::Connect (const char *url, Error *error_ptr) +{ + Clear(); + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Connect (url = %s)", this, url); + + if (m_connection_ap.get()) + return m_connection_ap->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); + + if (m_connection_ap.get()) + { + ConnectionStatus status = m_connection_ap->Disconnect (error_ptr); + m_connection_ap.reset(); + return status; + } + return eConnectionStatusNoConnection; +} + +bool +Communication::IsConnected () const +{ + if (m_connection_ap.get()) + return m_connection_ap->IsConnected (); + return false; +} + +bool +Communication::HasConnection () const +{ + return m_connection_ap.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::Write (dst = %p, dst_len = %zu, timeout_usec = %u) connection = %p", + this, dst, dst_len, timeout_usec, m_connection_ap.get()); + + if (m_read_thread != NULL) + { + // 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_ap.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. + if (m_connection_ap.get()) + { + status = m_connection_ap->BytesAvailable (timeout_usec, error_ptr); + if (status == eConnectionStatusSuccess) + return m_connection_ap->Read (dst, dst_len, 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_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Write (src = %p, src_len = %zu) connection = %p", + this, src, src_len, m_connection_ap.get()); + + if (m_connection_ap.get()) + return m_connection_ap->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 (m_read_thread != LLDB_INVALID_HOST_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); + return m_read_thread != LLDB_INVALID_HOST_THREAD; +} + +bool +Communication::StopReadThread (Error *error_ptr) +{ + if (m_read_thread == NULL) + 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); + + return Host::ThreadJoin (m_read_thread, NULL, error_ptr); +} + + +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.data(), 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) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::AppendBytesToCache (src = %p, src_len = %zu, broadcast = %i)", + this, bytes, len, broadcast); + if (bytes == NULL || len == 0) + 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 + { + 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, ConnectionStatus &status, Error *error_ptr) +{ + if (m_connection_ap.get()) + return m_connection_ap->Read (dst, dst_len, status, error_ptr); + return 0; +} + + +bool +Communication::ReadThreadIsRunning () +{ + return m_read_thread != NULL; +} + +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) + { + status = comm->BytesAvailable (UINT32_MAX, &error); + + if (status == eConnectionStatusSuccess) + { + size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), status, &error); + if (bytes_read > 0) + comm->AppendBytesToCache (buf, bytes_read, true); + } + + switch (status) + { + case eConnectionStatusSuccess: + break; + + case eConnectionStatusNoConnection: // No connection + case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + done = true; + // Fall through... + default: + case eConnectionStatusError: // Check GetError() for details + case eConnectionStatusTimedOut: // Request timed out + error.LogIfError(log, "%p Communication::BytesAvailable () => status = %i", p, status); + break; + } + } + if (log) + log->Printf ("%p Communication::ReadThread () thread exiting...", p); + + // Let clients know that this thread is exiting + comm->m_read_thread = LLDB_INVALID_HOST_THREAD; + 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) +{ + StopReadThread(NULL); + Disconnect (NULL); + m_connection_ap.reset(connection); +} diff --git a/lldb/source/Core/Connection.cpp b/lldb/source/Core/Connection.cpp new file mode 100644 index 000000000000..3c9bb8b1b7ed --- /dev/null +++ b/lldb/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/lldb/source/Core/ConnectionFileDescriptor.cpp b/lldb/source/Core/ConnectionFileDescriptor.cpp new file mode 100644 index 000000000000..b22f5ffa36d5 --- /dev/null +++ b/lldb/source/Core/ConnectionFileDescriptor.cpp @@ -0,0 +1,563 @@ +//===-- ConnectionFileDescriptor.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/ConnectionFileDescriptor.h" + +// C Includes +#include +#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/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; + +ConnectionFileDescriptor::ConnectionFileDescriptor () : + Connection(), + m_fd (-1), + m_should_close_fd (false) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, + "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", + this); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : + Connection(), + m_fd (fd), + m_should_close_fd (owns_fd) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, + "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", + this, fd, owns_fd); +} + + +ConnectionFileDescriptor::~ConnectionFileDescriptor () +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, + "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", + this); + Disconnect (NULL); +} + +bool +ConnectionFileDescriptor::IsConnected () const +{ + return m_fd >= 0; +} + +ConnectionStatus +ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::Connect (url = '%s')", + this, s); + + 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, "connect://")) + { + return SocketConnect (s + strlen("connect://"), error_ptr); + } + else if (strstr(s, "file://")) + { + // file:///PATH + const char *path = s + strlen("file://"); + m_fd = ::open (path, O_RDWR); + if (m_fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + int flags = ::fcntl (m_fd, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl (m_fd, F_SETFL, flags); + } + } + m_should_close_fd = true; + return eConnectionStatusSuccess; + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Unsupported connection URL: '%s'.\n", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("NULL connection URL."); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect (Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::Disconnect ()", + this); + if (m_should_close_fd == false) + { + m_fd = -1; + return eConnectionStatusSuccess; + } + return Close (m_fd, error_ptr); +} + +size_t +ConnectionFileDescriptor::Read (void *dst, size_t dst_len, 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 = %zu)...", + this, m_fd, dst, dst_len); + + Error error; + ssize_t bytes_read = ::read (m_fd, dst, dst_len); + if (bytes_read == 0) + { + error.SetErrorStringWithFormat("End-of-file.\n"); + status = eConnectionStatusLostConnection; + } + else if (bytes_read < 0) + { + error.SetErrorToErrno(); + } + else + { + error.Clear(); + } + + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %zu) => %zi, error = %s", + this, + m_fd, + dst, + dst_len, + 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. + status = eConnectionStatusSuccess; + return 0; + + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + 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 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; + } + +// if (log) +// error->Log(log, "::read ( %i, %p, %zu ) => %i", m_fd, dst, dst_len, bytesread); + Close (m_fd, NULL); + 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 = %zu)", this, src, src_len); + + if (!IsConnected ()) + { + if (error_ptr) + error_ptr->SetErrorString("Not connected."); + status = eConnectionStatusNoConnection; + return 0; + } + + + Error error; + + ssize_t bytes_sent = 0; + + if (m_is_socket) + bytes_sent = ::send (m_fd, src, src_len, 0); + else + bytes_sent = ::write (m_fd, src, src_len); + + if (bytes_sent < 0) + error.SetErrorToErrno (); + else + error.Clear (); + + if (log) + { + if (m_is_socket) + log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %zu, flags = 0) => %zi (error = %s)", + this, m_fd, src, src_len, bytes_sent, error.AsCString()); + else + log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %zu) => %zi (error = %s)", + this, m_fd, src, src_len, bytes_sent, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... + } + + Close (m_fd, NULL); + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + 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; + } + + while (IsConnected()) + { + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (m_fd, &read_fds); + int nfds = m_fd + 1; + + Error error; + + + if (log) + log->Printf("%p ConnectionFileDescriptor::Write() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p)...", + this, nfds, m_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) + log->Printf("%p ConnectionFileDescriptor::Write() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p) => %d, error = %s", + this, nfds, m_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. + 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) + { + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorString("Not connected."); + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + bool success = true; + if (fd >= 0) + { + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::Close (fd = %i)", + this, + fd); + + success = ::close (fd) == 0; + 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(); + } + fd = -1; + } + m_is_socket = false; + if (success) + return eConnectionStatusSuccess; + else + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::SocketListen (port = %i)", + this, listen_port_num); + + Close (m_fd, false); + m_is_socket = true; + 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); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof sa); + sa.sin_len = sizeof sa; + sa.sin_family = AF_INET; + sa.sin_port = htons (listen_port_num); + sa.sin_addr.s_addr = htonl (INADDR_ANY); + + int err = ::bind (listen_port, (struct sockaddr *) &sa, sizeof(sa)); + 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 = ::accept (listen_port, NULL, 0); + if (m_fd == -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, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketConnect (const char *host_and_port, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::SocketConnect (host/port = %s)", + this, host_and_port); + Close (m_fd, false); + m_is_socket = true; + + RegularExpression regex ("([^:]+):([0-9]+)"); + if (regex.Execute (host_and_port, 2) == false) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid host:port specification: '%s'.\n", host_and_port); + return eConnectionStatusError; + } + std::string host_str; + std::string port_str; + if (regex.GetMatchAtIndex (host_and_port, 1, host_str) == false || + regex.GetMatchAtIndex (host_and_port, 2, port_str) == false) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid host:port specification '%s'.\n", host_and_port); + return eConnectionStatusError; + } + + int32_t port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN); + if (port == INT32_MIN) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid port '%s'.\n", port_str.c_str()); + return eConnectionStatusError; + } + // Create the socket + m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + m_should_close_fd = true; + + // Enable local address reuse + SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::bzero (&sa, sizeof (sa)); + sa.sin_len = 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'.\n", host_str.c_str()); + } + Close (m_fd, false); + return eConnectionStatusError; + } + } + + if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa))) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (m_fd, false); + return eConnectionStatusError; + } + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +int +ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) +{ + return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value)); +} + + diff --git a/lldb/source/Core/ConstString.cpp b/lldb/source/Core/ConstString.cpp new file mode 100644 index 000000000000..c2d09c76461b --- /dev/null +++ b/lldb/source/Core/ConstString.cpp @@ -0,0 +1,480 @@ +//===-- 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; + + +//---------------------------------------------------------------------- +// The global string pool is implemented as a hash_map that maps +// std::string objects to a uint32_t reference count. +// +// In debug builds the value that is stored in the ConstString objects is +// a C string that is owned by one of the std::string objects in the +// hash map. This was done for visibility purposes when debugging as +// gcc was often generating insufficient debug info for the +// iterator objects. +// +// In release builds, the value that is stored in the ConstString objects +// is the iterator into the ConstString::HashMap. This is much faster when +// it comes to modifying the reference count, and removing strings from +// the pool. +//---------------------------------------------------------------------- +class Pool +{ +public: + //------------------------------------------------------------------ + // Default constructor + // + // Initialize the member variables and create the empty string. + //------------------------------------------------------------------ + Pool () : + m_mutex (Mutex::eMutexTypeRecursive), + m_string_map () + { + } + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~Pool () + { + } + + + static llvm::StringMapEntry & + GetStringMapEntryFromKeyData (const char *keyData) + { + char *ptr = const_cast(keyData) - sizeof (llvm::StringMapEntry); + return *reinterpret_cast*>(ptr); + } + + size_t + GetConstCStringLength (const char *ccstr) + { + if (ccstr) + { + llvm::StringMapEntry&entry = GetStringMapEntryFromKeyData (ccstr); + return entry.getKey().size(); + } + return 0; + } + + const char * + GetConstCString (const char *cstr) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr); + llvm::StringMapEntry& entry = m_string_map.GetOrCreateValue (string_ref); + const char *ccstr = entry.getKeyData(); + llvm::StringMapEntry&reconstituted_entry = GetStringMapEntryFromKeyData (ccstr); + assert (&entry == &reconstituted_entry); + return ccstr; + } + return NULL; + } + + const char * + GetConstCStringWithLength (const char *cstr, int cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr, cstr_len); + llvm::StringMapEntry& entry = m_string_map.GetOrCreateValue (string_ref); + const char *ccstr = entry.getKeyData(); + llvm::StringMapEntry&reconstituted_entry = GetStringMapEntryFromKeyData (ccstr); + assert (&entry == &reconstituted_entry); + return ccstr; + } + return NULL; + } + + const char * + GetConstTrimmedCStringWithLength (const char *cstr, int cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + int actual_cstr_len = strlen (cstr); + llvm::StringRef string_ref (cstr, std::min(actual_cstr_len, cstr_len)); + llvm::StringMapEntry& entry = m_string_map.GetOrCreateValue (string_ref); + const char *ccstr = entry.getKeyData(); + llvm::StringMapEntry&reconstituted_entry = GetStringMapEntryFromKeyData (ccstr); + assert (&entry == &reconstituted_entry); + return ccstr; + } + return NULL; + } + + //------------------------------------------------------------------ + // Return the size in bytes that this object and any items in its + // collection of uniqued strings + reference 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(llvm::StringMapEntry) + pos->getKey().size(); + } + return mem_size; + } + +protected: + //------------------------------------------------------------------ + // Typedefs + //------------------------------------------------------------------ + typedef llvm::StringMap StringPool; + 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. +//---------------------------------------------------------------------- +static Pool & +StringPool() +{ + static Pool string_pool; + return string_pool; +} + +//---------------------------------------------------------------------- +// Default constructor +// +// Initializes the string to an empty string. +//---------------------------------------------------------------------- +ConstString::ConstString () : + m_string (NULL) +{ +} + +//---------------------------------------------------------------------- +// Copy constructor +// +// Copies the string value in "rhs" and retains an extra reference +// to the string value in the string pool. +//---------------------------------------------------------------------- +ConstString::ConstString (const ConstString& rhs) : + m_string (rhs.m_string) +{ +} + +//---------------------------------------------------------------------- +// 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 does +// exist, it retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +ConstString::ConstString (const char *cstr) : + m_string (StringPool().GetConstCString (cstr)) +{ +} + +//---------------------------------------------------------------------- +// Construct with C String value with max length +// +// Constructs this object with a C string with a length. If +// the length of the string is greather than "cstr_len", 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. +// +// If the C string already exists in the global string pool, it +// retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +ConstString::ConstString (const char *cstr, size_t cstr_len) : + m_string (StringPool().GetConstCStringWithLength (cstr, cstr_len)) +{ +} + +//---------------------------------------------------------------------- +// Destructor +// +// Decrements the reference count on the contained string, and if +// the resulting reference count is zero, then the string is removed +// from the string pool. If the reference count is still greater +// than zero, the string will remain in the string pool +//---------------------------------------------------------------------- +ConstString::~ConstString () +{ +} + +//---------------------------------------------------------------------- +// Convert to pointer operator. This allows code to check any +// ConstString objects to see if they contain anything (not empty) +// valid using code such as: +// +// ConstString str(...); +// if (str) +// { ... +//---------------------------------------------------------------------- +ConstString::operator void*() const +{ + return IsEmpty() ? NULL : const_cast(this); +} + +//---------------------------------------------------------------------- +// Assignment operator +// +// Assigns the string in this object with the value from "rhs" +// and increments the reference count of that string. +// +// The previously contained string will be get its reference count +// decremented and removed from the string pool if its reference +// count reaches zero. +//---------------------------------------------------------------------- +const ConstString& +ConstString::operator=(const ConstString& rhs) +{ + m_string = rhs.m_string; + return *this; +} + +//---------------------------------------------------------------------- +// Equal to operator +// +// Returns true if this string is equal to that in "rhs". This is +// very fast as it results in a pointer comparison since all strings +// are in a uniqued and reference counted string pool. +//------------------------------------------------------------------ +bool +ConstString::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; +} + +bool +ConstString::operator != (const ConstString& rhs) const +{ + return m_string != rhs.m_string; +} + +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 the string value "str" to the stream "s" +//---------------------------------------------------------------------- +Stream& +lldb_private::operator << (Stream& s, const ConstString& str) +{ + const char *cstr = str.GetCString(); + if (cstr) + s << cstr; + + return s; +} + +//---------------------------------------------------------------------- +// Get the value of the contained string as a NULL terminated C +// string value. Return "fail_value" if the string is empty. +//---------------------------------------------------------------------- +const char * +ConstString::AsCString(const char *fail_value) const +{ + if (m_string == NULL) + return fail_value; + return m_string; +} + +const char * +ConstString::GetCString () const +{ + return m_string; +} + +size_t +ConstString::GetLength () const +{ + return StringPool().GetConstCStringLength (m_string); +} + + +//---------------------------------------------------------------------- +// Clear any contained string and reset the value to the an empty +// string value. +// +// The previously contained string will be get its reference count +// decremented and removed from the string pool if its reference +// count reaches zero. +//---------------------------------------------------------------------- +void +ConstString::Clear () +{ + m_string = NULL; +} + +//---------------------------------------------------------------------- +// Compare two string objects. +// +// Returns: +// -1 if a < b +// 0 if a == b +// 1 if a > b +//---------------------------------------------------------------------- +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 +} + +//---------------------------------------------------------------------- +// Dump the string value to the stream "s". If the contained string +// is empty, print "fail_value" to the stream instead. If +// "fail_value" is NULL, then nothing will be dumped to the +// stream. +//---------------------------------------------------------------------- +void +ConstString::Dump(Stream *s, const char *fail_value) const +{ + const char *cstr = AsCString (fail_value); + if (cstr) + s->PutCString (cstr); +} + +//---------------------------------------------------------------------- +// Dump extra debug information to the stream "s". +//---------------------------------------------------------------------- +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 = %zu", (int)sizeof(void*) * 2, this, parens, cstr, parens, cstr_len); +} + +//---------------------------------------------------------------------- +// Returns true if the contained string is empty. +//---------------------------------------------------------------------- +bool +ConstString::IsEmpty() const +{ + return m_string == NULL || m_string[0] == '\0'; +} + +//---------------------------------------------------------------------- +// Set the string value in the object by uniquing the "cstr" string +// value in our global string pool. +// +// If the C string already exists in the global string pool, it +// retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +void +ConstString::SetCString (const char *cstr) +{ + m_string = StringPool().GetConstCString (cstr); +} + +//---------------------------------------------------------------------- +// Set the string value in the object by uniquing "cstr_len" bytes +// starting at the "cstr" string value in our global string pool. +// If trim is true, then "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 trim is false, then this allows strings +// with NULL characters ('\0') to be added to the string pool. +// +// If the C string already exists in the global string pool, it +// retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +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); +} + +//---------------------------------------------------------------------- +// Return the size in bytes that this object takes in memory. The +// resulting size will not include any of the C string values from +// the global string pool (see StaticMemorySize ()). +//---------------------------------------------------------------------- +size_t +ConstString::MemorySize() const +{ + return sizeof(ConstString); +} + +//---------------------------------------------------------------------- +// Reports the the size in bytes of all shared C string values, +// containers and reference count values as a byte size for the +// entire string pool. +//---------------------------------------------------------------------- +size_t +ConstString::StaticMemorySize() +{ + // Get the size of the static string pool + return StringPool().MemorySize(); +} diff --git a/lldb/source/Core/DataBufferHeap.cpp b/lldb/source/Core/DataBufferHeap.cpp new file mode 100644 index 000000000000..a93427ff8580 --- /dev/null +++ b/lldb/source/Core/DataBufferHeap.cpp @@ -0,0 +1,105 @@ +//===-- 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 (size_t n, uint8_t ch) : + m_data(n, ch) +{ +} + +//---------------------------------------------------------------------- +// Initialize this class with a copy of the "n" bytes from the "bytes" +// buffer. +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap (const void *src, size_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. +//---------------------------------------------------------------------- +size_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. +//---------------------------------------------------------------------- +size_t +DataBufferHeap::SetByteSize (size_t new_size) +{ + m_data.resize(new_size); + return m_data.size(); +} + +void +DataBufferHeap::CopyData (const void *src, size_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(); +} + + + diff --git a/lldb/source/Core/DataBufferMemoryMap.cpp b/lldb/source/Core/DataBufferMemoryMap.cpp new file mode 100644 index 000000000000..c8ad5492f51d --- /dev/null +++ b/lldb/source/Core/DataBufferMemoryMap.cpp @@ -0,0 +1,214 @@ +//===-- 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 "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Host/Host.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +DataBufferMemoryMap::DataBufferMemoryMap() : + m_mmap_addr(NULL), + m_mmap_size(0), + m_data(NULL), + m_size(0), + m_error() +{ +} + +//---------------------------------------------------------------------- +// 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. +//---------------------------------------------------------------------- +size_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) + { + ::munmap((void *)m_mmap_addr, m_mmap_size); + m_mmap_addr = NULL; + m_mmap_size = 0; + m_data = NULL; + m_size = 0; + } + m_error.Clear(); +} + + +const Error & +DataBufferMemoryMap::GetError() const +{ + return m_error; +} + +//---------------------------------------------------------------------- +// Memory map "length" bytes from "file" starting "offset" +// bytes into the file. If "length" is set to SIZE_T_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* file, off_t offset, size_t length) +{ + if (file != NULL) + { + char path[PATH_MAX]; + if (file->GetPath(path, sizeof(path))) + { + int fd = ::open(path, O_RDONLY, 0); + if (fd >= 0) + { + MemoryMapFromFileDescriptor (fd, offset, length); + ::close(fd); + return GetByteSize(); + } + else + { + m_error.SetErrorToErrno(); + return 0; + } + } + } + // 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_T_MAX, then map as many bytes +// as possible. +// +// RETURNS +// Number of bytes mapped starting from the requested offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, off_t offset, size_t length) +{ + Clear(); + if (fd >= 0) + { + struct stat stat; + if (::fstat(fd, &stat) == 0) + { + if ((stat.st_mode & S_IFREG) && (stat.st_size > offset)) + { + if (length == SIZE_T_MAX) + length = stat.st_size - offset; + + // Cap the length if too much data was requested + if (length > stat.st_size - offset) + length = stat.st_size - offset; + + if (length > 0) + { + m_mmap_addr = (uint8_t *)::mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, offset); + + if (m_mmap_addr == (void*)-1) + { + m_error.SetErrorToErrno (); + if (m_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_READ, MAP_FILE | MAP_SHARED, 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 + m_error.Clear (); + + // 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; + } + } + } + } + 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; + } + } + } + } + + ::close (fd); + } + return GetByteSize (); +} diff --git a/lldb/source/Core/DataExtractor.cpp b/lldb/source/Core/DataExtractor.cpp new file mode 100644 index 000000000000..236ec187f5f9 --- /dev/null +++ b/lldb/source/Core/DataExtractor.cpp @@ -0,0 +1,1517 @@ +//===-- 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 "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBuffer.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" + +using namespace lldb; +using namespace lldb_private; + +#define NON_PRINTABLE_CHAR '.' +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor () : + m_start (NULL), + m_end (NULL), + m_byte_order(eByteOrderHost), + 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, 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 DataExtractor objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (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); +} + +//---------------------------------------------------------------------- +// 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, uint32_t offset, uint32_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)) + { + uint32_t bytes_available = data.GetByteSize() - offset; + if (length > bytes_available) + length = bytes_available; + SetData(data, offset, length); + } +} + +//---------------------------------------------------------------------- +// 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 = eByteOrderHost; + m_addr_size = 4; + m_data_sp.reset(); +} + +//------------------------------------------------------------------ +// Returns the total number of bytes that this object refers to +//------------------------------------------------------------------ +size_t +DataExtractor::GetByteSize () const +{ + return m_end - m_start; +} + +//------------------------------------------------------------------ +// 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; +} + +//------------------------------------------------------------------ +// Returns true if OFFSET is a valid offset into the data in this +// object. +//------------------------------------------------------------------ +bool +DataExtractor::ValidOffset (uint32_t offset) const +{ + return offset < GetByteSize(); +} + +//------------------------------------------------------------------ +// Returns true if there are LENGTH bytes availabe starting OFFSET +// into the data that is in this object. +//------------------------------------------------------------------ +bool +DataExtractor::ValidOffsetForDataOfSize (uint32_t offset, uint32_t length) const +{ + size_t size = GetByteSize(); + if (offset >= size) + return false; // offset isn't valid + + if (length == 0) + return true; // No bytes requested at this offset, return true + + // If we flip the bits in offset we can figure out how + // many bytes we have left before "offset + length" + // could overflow when doing unsigned arithmetic. + if (length > ~offset) + return false; // unsigned overflow + + // Make sure "offset + length" is a valid offset as well. + // length must be greater than zero for this to be a + // valid expression, and we have already checked for this. + return ((offset + length) <= size); +} + +//------------------------------------------------------------------ +// 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 * +DataExtractor::GetDataStart () const +{ + return m_start; +} +//------------------------------------------------------------------ +// Returns a pointer to the byte past the last byte contained in +// this object's data, or NULL of there is no data in this object. +//------------------------------------------------------------------ +const uint8_t * +DataExtractor::GetDataEnd () const +{ + return m_end; +} + +//------------------------------------------------------------------ +// Returns true if this object will endian swap values as it +// extracts data. +//------------------------------------------------------------------ +ByteOrder +DataExtractor::GetByteOrder () const +{ + return m_byte_order; +} +//------------------------------------------------------------------ +// Set wether this object will endian swap values as it extracts +// data. +//------------------------------------------------------------------ +void +DataExtractor::SetByteOrder (ByteOrder endian) +{ + m_byte_order = endian; +} + + +//------------------------------------------------------------------ +// Return the size in bytes of any address values this object will +// extract +//------------------------------------------------------------------ +uint8_t +DataExtractor::GetAddressByteSize () const +{ + return m_addr_size; +} + +//------------------------------------------------------------------ +// Set the size in bytes that will be used when extracting any +// address values from data contained in this object. +//------------------------------------------------------------------ +void +DataExtractor::SetAddressByteSize (uint8_t addr_size) +{ + m_addr_size = addr_size; +} + +//---------------------------------------------------------------------- +// 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 +DataExtractor::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 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". +//---------------------------------------------------------------------- +uint32_t +DataExtractor::SetData (const DataExtractor& data, uint32_t data_offset, uint32_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. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::SetData (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. +//---------------------------------------------------------------------- +uint8_t +DataExtractor::GetU8 (uint32_t *offset_ptr) const +{ + uint8_t val = 0; + if ( m_start < m_end ) + { + val = m_start[*offset_ptr]; + *offset_ptr += sizeof(val); + } + return val; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr, void *dst, uint32_t count) const +{ + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, count) ) + { + // Copy the data into the buffer + memcpy (dst, m_start + offset, count); + // Advance the offset + *offset_ptr += 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 (uint32_t *offset_ptr) const +{ + uint16_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt16(m_start, offset); + else + val = _OSReadInt16 (m_start, offset); + + // Advance the offset + *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 (uint32_t *offset_ptr, void *void_dst, uint32_t count) const +{ + uint16_t *dst = (uint16_t *)void_dst; + const size_t value_size = sizeof(*dst); + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, value_size * count) ) + { + uint16_t *value_ptr; + uint16_t *end = dst + count; + if (m_byte_order != eByteOrderHost) + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = OSReadSwapInt16 (m_start, offset); + } + else + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = _OSReadInt16 (m_start, offset); + } + + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of success + return 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 (uint32_t *offset_ptr) const +{ + uint32_t val = 0; + register uint32_t offset = *offset_ptr; + + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt32 (m_start, offset); + else + val = _OSReadInt32 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + 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 (uint32_t *offset_ptr, void *void_dst, uint32_t count) const +{ + uint32_t *dst = (uint32_t *)void_dst; + const size_t value_size = sizeof(*dst); + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, value_size * count)) + { + uint32_t *value_ptr; + uint32_t *end = dst + count; + if (m_byte_order != eByteOrderHost) + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = OSReadSwapInt32 (m_start, offset); + + } + else + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = _OSReadInt32 (m_start, offset); + } + + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of success + return 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 (uint32_t *offset_ptr) const +{ + uint64_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt64 (m_start, offset); + else + val = _OSReadInt64 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + 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 (uint32_t *offset_ptr, void *void_dst, uint32_t count) const +{ + uint64_t *dst = (uint64_t *)void_dst; + const size_t value_size = sizeof(uint64_t); + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, value_size * count)) + { + uint64_t *value_ptr; + uint64_t *end = dst + count; + if (m_byte_order != eByteOrderHost) + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = OSReadSwapInt64 (m_start, offset); + + } + else + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = _OSReadInt64 (m_start, offset); + } + + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of success + return 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 (uint32_t *offset_ptr, uint32_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!"); + 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 (uint32_t *offset_ptr, uint32_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!"); + break; + } + return 0; +} + +int64_t +DataExtractor::GetMaxS64 (uint32_t *offset_ptr, uint32_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!"); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64Bitfield (uint32_t *offset_ptr, uint32_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 = ((1 << bitfield_bit_size) - 1); + uval64 &= bitfield_mask; + } + return uval64; +} + +int64_t +DataExtractor::GetMaxS64Bitfield (uint32_t *offset_ptr, uint32_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 = ((1 << bitfield_bit_size) - 1); + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & (1 << (bitfield_bit_size - 1))) + sval64 |= ~bitfield_mask; + } + return sval64; +} + + +float +DataExtractor::GetFloat (uint32_t *offset_ptr) const +{ + uint32_t val = 0; + register uint32_t offset = *offset_ptr; + + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt32 (m_start, offset); + else + val = _OSReadInt32 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return *((float *)&val); +} + +double +DataExtractor::GetDouble (uint32_t *offset_ptr) const +{ + uint64_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt64 (m_start, offset); + else + val = _OSReadInt64 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return *((double *)&val); + +} + + +long double +DataExtractor::GetLongDouble (uint32_t *offset_ptr) const +{ + if (sizeof(long double) == sizeof(uint64_t)) + { + uint64_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt64 (m_start, offset); + else + val = _OSReadInt64 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return *((long double *)&val); + } + return 0.0; +} + + +//------------------------------------------------------------------ +// Extract a single address from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted address +// comes from the "this->m_addr_size" member variable and should be +// set correctly prior to extracting any address values. +// +// RETURNS the address that was extracted, or zero on failure. +//------------------------------------------------------------------ +uint64_t +DataExtractor::GetAddress (uint32_t *offset_ptr) const +{ + return GetMaxU64 (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 (uint32_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 (uint32_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_GNU_EH_PE_omit) + return ULONG_LONG_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_GNU_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_GNU_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_GNU_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_GNU_EH_PE_funcrel: + signExtendValue = true; + break; + + case DW_GNU_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_GNU_EH_PE_MASK_ENCODING) + { + case DW_GNU_EH_PE_absptr : + { + addressValue = GetAddress (offset_ptr); +// if (data_relocs) +// addressValue = data_relocs->Relocate(*offset_ptr - addr_size, *this, addressValue); + } + break; + case DW_GNU_EH_PE_uleb128 : addressValue = GetULEB128(offset_ptr); break; + case DW_GNU_EH_PE_udata2 : addressValue = GetU16(offset_ptr); break; + case DW_GNU_EH_PE_udata4 : addressValue = GetU32(offset_ptr); break; + case DW_GNU_EH_PE_udata8 : addressValue = GetU64(offset_ptr); break; + case DW_GNU_EH_PE_sleb128 : addressValue = GetSLEB128(offset_ptr); break; + case DW_GNU_EH_PE_sdata2 : addressValue = (int16_t)GetU16(offset_ptr); break; + case DW_GNU_EH_PE_sdata4 : addressValue = (int32_t)GetU32(offset_ptr); break; + case DW_GNU_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 (uint32_t offset, uint32_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 && ValidOffsetForDataOfSize(offset, length) ) + return m_start + offset; + return NULL; +} + +//---------------------------------------------------------------------- +// Returns a pointer to a bytes in this object's data at the offset +// pointed to by "offset_ptr". If "length" is zero or too large, +// then the offset pointed to by "offset_ptr" will not be updated +// and NULL will be returned. +// +// Returns a pointer to the data if the offset and length are valid, +// or NULL otherwise. +//---------------------------------------------------------------------- +const void* +DataExtractor::GetData (uint32_t *offset_ptr, uint32_t length) const +{ + const uint8_t* bytes = NULL; + register uint32_t offset = *offset_ptr; + if ( length > 0 && ValidOffsetForDataOfSize(offset, length) ) + { + bytes = m_start + offset; + *offset_ptr = offset + length; + } + return bytes; +} + +//---------------------------------------------------------------------- +// Extracts a AsCString (fixed length, or variable length) from +// the data at the offset pointed to by "offset_ptr". If +// "length" is zero, then a variable length NULL terminated C +// string will be extracted from the data the "offset_ptr" will be +// updated with the offset of the byte that follows the NULL +// terminator byte. If "length" is greater than zero, then +// the function will make sure there are "length" bytes +// available in the current data and if so, return a valid pointer. +// +// If the offset pointed to by "offset_ptr" is out of bounds, or if +// "length" is non-zero and there aren't enough avaialable +// bytes, NULL will be returned and "offset_ptr" will not be +// updated. +//---------------------------------------------------------------------- +const char* +DataExtractor::GetCStr (uint32_t *offset_ptr) const +{ + const char *s = NULL; + if ( m_start < m_end ) + { + s = (char*)m_start + *offset_ptr; + + size_t length = strlen(s) + 1; + + if (!ValidOffsetForDataOfSize(*offset_ptr, length)) + return NULL; + + // Advance the offset + *offset_ptr += length; + } + return s; +} + +//------------------------------------------------------------------ +// Peeks at a string in the contained data. No verification is done +// to make sure the entire string lies within the bounds of this +// object's data, only "offset" is verified to be a valid offset. +// +// Returns a valid C string pointer if "offset" is a valid offset in +// this object's data, else NULL is returned. +//------------------------------------------------------------------ +const char * +DataExtractor::PeekCStr (uint32_t offset) const +{ + if (ValidOffset (offset)) + return (const char*)m_start + offset; + return NULL; +} + +//---------------------------------------------------------------------- +// Extracts an unsigned LEB128 number from this object's data +// starting at the offset pointed to by "offset_ptr". The offset +// pointed to by "offset_ptr" will be updated with the offset of the +// byte following the last extracted byte. +// +// Returned the extracted integer value. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetULEB128 (uint32_t *offset_ptr) const +{ + uint64_t result = 0; + if ( m_start < m_end ) + { + int shift = 0; + const uint8_t *src = m_start + *offset_ptr; + uint8_t byte; + int bytecount = 0; + + while (src < m_end) + { + bytecount++; + byte = *src++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + + *offset_ptr += bytecount; + } + return result; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr) const +{ + int64_t result = 0; + + if ( m_start < m_end ) + { + int shift = 0; + int size = sizeof (uint32_t) * 8; + const uint8_t *src = m_start + *offset_ptr; + + uint8_t byte; + int bytecount = 0; + + while (src < m_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; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr) const +{ + uint32_t bytes_consumed = 0; + if ( m_start < m_end ) + { + const uint8_t *start = m_start + *offset_ptr; + const uint8_t *src = start; + + while ((src < m_end) && (*src++ & 0x80)) + ++bytes_consumed; + + *offset_ptr += src - start; + } + return bytes_consumed; +} + +uint32_t +DataExtractor::Dump +( + Stream *s, + uint32_t start_offset, + lldb::Format item_format, + uint32_t item_byte_size, + uint32_t item_count, + uint32_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 +) const +{ + if (s == NULL) + return start_offset; + + uint32_t offset; + uint32_t count; + uint32_t line_start_offset; + + if (item_format == eFormatPointer) + { + if (item_byte_size != 4 && item_byte_size != 8) + item_byte_size = s->GetAddressByteSize(); + } + + for (offset = start_offset, line_start_offset = start_offset, 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", (num_per_line - (offset - line_start_offset)) * 3 + 2, ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + } + s->EOL(); + } + if (base_addr != LLDB_INVALID_ADDRESS) + s->Printf ("0x%8.8llx: ", (uint64_t)(base_addr + (offset - start_offset))); + line_start_offset = offset; + } + else + if (item_format != eFormatChar && + item_format != eFormatCharPrintable && + count > 0) + { + s->PutChar(' '); + } + + uint32_t i; + switch (item_format) + { + case eFormatBoolean: + s->Printf ("%s", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset) ? "true" : "false"); + break; + + case eFormatBinary: + { + uint64_t uval64 = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + std::string binary_value(std::bitset<64>(uval64).to_string()); + 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); + } + 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: + { + // If we are only printing one character surround it with single + // quotes + if (item_count == 1 && item_format == eFormatChar) + s->PutChar('\''); + + uint8_t ch = GetU8(&offset); + if (isprint(ch)) + s->Printf ("%c", ch); + else if (item_format == eFormatChar) + { + switch (ch) + { + case '\e': s->Printf ("\\e", (uint8_t)ch); break; + case '\a': s->Printf ("\\a", ch); break; + case '\b': s->Printf ("\\b", ch); break; + case '\f': s->Printf ("\\f", ch); break; + case '\n': s->Printf ("\\n", ch); break; + case '\r': s->Printf ("\\r", ch); break; + case '\t': s->Printf ("\\t", ch); break; + case '\v': s->Printf ("\\v", ch); break; + case '\0': s->Printf ("\\0", ch); break; + default: s->Printf ("\\x%2.2x", 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 eFormatComplex: + if (sizeof(float) * 2 == item_byte_size) + { + uint32_t a32 = GetU32(&offset); + uint32_t b32 = GetU32(&offset); + + s->Printf ("%g + %gi", a32, b32); + } + else if (sizeof(double) * 2 == item_byte_size) + { + uint64_t a64 = GetU64(&offset); + uint64_t b64 = GetU64(&offset); + + s->Printf ("%lg + %lgi", a64, b64); + } + else if (sizeof(long double) * 2 == item_byte_size && sizeof(long double) <= sizeof(uint64_t)) + { + uint64_t a64 = GetU64(&offset); + uint64_t b64 = GetU64(&offset); + s->Printf ("%Lg + %Lgi", a64, b64); + } + break; + + case eFormatDecimal: + if (item_byte_size <= 8); + s->Printf ("%lld", GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatUnsigned: + if (item_byte_size <= 8); + s->Printf ("%llu", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatOctal: + if (item_byte_size <= 8); + s->Printf ("0%llo", GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatEnum: + // Print enum value as a signed integer when we don't get the enum type + s->Printf ("%lld", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatCString: + { + const char *cstr = GetCStr(&offset); + if (cstr) + s->Printf("\"%s\"", cstr); + else + { + s->Printf("NULL", cstr); + offset = UINT32_MAX; + } + } + break; + + + case eFormatPointer: + s->Address(GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset), sizeof (addr_t)); + break; + + case eFormatHex: + if (item_byte_size <= 8) + { + s->Printf("0x%*.*llx", 2 * item_byte_size, 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"); + int32_t start_idx, end_idx, delta; + if (m_byte_order == eByteOrderBig) + { + start_idx = offset; + end_idx = offset + item_byte_size; + delta = 1; + } + else + { + start_idx = offset + item_byte_size - 1; + end_idx = -1; + delta = -1; + } + const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); + if (bytes) + { + for (int32_t idx = start_idx; idx != end_idx; idx += delta) + s->Printf("%2.2x", bytes[idx]); + } + } + break; + + case eFormatFloat: + if (sizeof(float) == item_byte_size) + { + uint32_t a32 = GetU32(&offset); + s->Printf ("%g", (double)(*((float *)&a32))); + } + else if (sizeof(double) == item_byte_size) + { + uint64_t a64 = GetU64(&offset); + s->Printf ("%lg", (*((double *)&a64))); + } + else if (sizeof(long double) == item_byte_size && sizeof(long double) <= sizeof(uint64_t)) + { + uint64_t a64 = GetU64(&offset); + s->Printf ("%Lg", (*((long double *)&a64))); + } + break; + + case eFormatUnicode16: + s->Printf("0x%4.4x", GetU16 (&offset)); + break; + + case eFormatUnicode32: + s->Printf("0x%8.8x", GetU32 (&offset)); + break; + + case eFormatVectorOfChar: + s->PutChar('{'); + offset = Dump (s, start_offset, eFormatChar, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt8: + s->PutChar('{'); + offset = Dump (s, start_offset, eFormatDecimal, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt8: + s->PutChar('{'); + offset = Dump (s, start_offset, eFormatHex, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt16: + s->PutChar('{'); + offset = Dump (s, start_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, start_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, start_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, start_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, start_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, start_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 eFormatVectorOfFloat32: + s->PutChar('{'); + offset = Dump (s, start_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, start_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, start_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", (num_per_line - (offset - line_start_offset)) * 3 + 2, ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, UINT32_MAX, 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(). +//---------------------------------------------------------------------- +uint32_t +DataExtractor::PutToLog +( + Log *log, + uint32_t start_offset, + uint32_t length, + uint64_t base_addr, + uint32_t num_per_line, + DataExtractor::Type type, + const char *format +) const +{ + if (log == NULL) + return start_offset; + + uint32_t offset; + uint32_t end_offset = offset + length; + uint32_t count; + StreamString sstr; + for (offset = start_offset, 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.8llx:", (uint64_t)(base_addr + (offset - start_offset))); + } + + switch (type) + { + default: + 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.16llx", GetU64(&offset)); break; + case TypePointer: sstr.Printf (format ? format : " 0x%llx", GetAddress(&offset)); break; + case TypeULEB128: sstr.Printf (format ? format : " 0x%llx", GetULEB128(&offset)); break; + case TypeSLEB128: sstr.Printf (format ? format : " %lld", 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, uint32_t offset) const +{ + if (s) + { + const uint8_t *uuid_data = PeekData(offset, 16); + if ( uuid_data ) + { + UUID uuid(uuid_data, 16); + uuid.Dump(s); + } + else + { + s->Printf("", offset); + } + } +} + + diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp new file mode 100644 index 000000000000..c106d838fed4 --- /dev/null +++ b/lldb/source/Core/Debugger.cpp @@ -0,0 +1,434 @@ +//===-- 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-private.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + + +using namespace lldb; +using namespace lldb_private; + +int Debugger::g_shared_debugger_refcount = 0; +bool Debugger::g_in_terminate = false; + +Debugger::DebuggerSP & +Debugger::GetDebuggerSP () +{ + static DebuggerSP g_shared_debugger_sp; + return g_shared_debugger_sp; +} + +void +Debugger::Initialize () +{ + g_shared_debugger_refcount++; + if (GetDebuggerSP().get() == NULL) + { + GetDebuggerSP().reset (new Debugger()); + lldb_private::Initialize(); + GetDebuggerSP()->GetCommandInterpreter().Initialize(); + } +} + +void +Debugger::Terminate () +{ + g_shared_debugger_refcount--; + if (g_shared_debugger_refcount == 0) + { + // Because Terminate is called also in the destructor, we need to make sure + // that none of the calls to GetSharedInstance leads to a call to Initialize, + // thus bumping the refcount back to 1 & causing Debugger::~Debugger to try to + // re-terminate. So we use g_in_terminate to indicate this condition. + // When we can require at least Initialize to be called, we won't have to do + // this since then the GetSharedInstance won't have to auto-call Initialize... + + g_in_terminate = true; + int num_targets = GetDebuggerSP()->GetTargetList().GetNumTargets(); + for (int i = 0; i < num_targets; i++) + { + ProcessSP process_sp(GetDebuggerSP()->GetTargetList().GetTargetAtIndex (i)->GetProcessSP()); + if (process_sp) + process_sp->Destroy(); + } + GetDebuggerSP()->DisconnectInput(); + lldb_private::WillTerminate(); + GetDebuggerSP().reset(); + } +} + +Debugger & +Debugger::GetSharedInstance() +{ + // Don't worry about thread race conditions with the code below as + // lldb_private::Initialize(); does this in a thread safe way. I just + // want to avoid having to lock and unlock a mutex in + // lldb_private::Initialize(); every time we want to access the + // Debugger shared instance. + + // FIXME: We intend to require clients to call Initialize by hand (since they + // will also have to call Terminate by hand.) But for now it is not clear where + // we can reliably call these in JH. So the present version initializes on first use + // here, and terminates in the destructor. + if (g_shared_debugger_refcount == 0 && !g_in_terminate) + Initialize(); + + assert(GetDebuggerSP().get()!= NULL); + return *(GetDebuggerSP().get()); +} + +Debugger::Debugger () : + m_input_comm("debugger.input"), + m_input_file (), + m_output_file (), + m_error_file (), + m_async_execution (true), + m_target_list (), + m_listener ("lldb.Debugger"), + m_source_manager (), + m_command_interpreter (eScriptLanguageDefault, false, &m_listener, m_source_manager), + m_input_readers (), + m_input_reader_data () +{ +} + +Debugger::~Debugger () +{ + // FIXME: + // Remove this once this version of lldb has made its way through a build. + Terminate(); +} + + +bool +Debugger::GetAsyncExecution () +{ + return m_async_execution; +} + +void +Debugger::SetAsyncExecution (bool async_execution) +{ + static bool value_has_been_set = false; + + if (!value_has_been_set) + { + value_has_been_set = true; + m_async_execution = async_execution; + m_command_interpreter.SetSynchronous (!async_execution); + } +} + +void +Debugger::DisconnectInput() +{ + m_input_comm.Clear (); +} + +void +Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_input_file.SetFileHandle (fh, tranfer_ownership); + if (m_input_file.GetFileHandle() == NULL) + m_input_file.SetFileHandle (stdin, false); + + // Disconnect from any old connection if we had one + m_input_comm.Disconnect (); + m_input_comm.SetConnection (new ConnectionFileDescriptor (::fileno (GetInputFileHandle()), true)); + m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); + + Error error; + if (m_input_comm.StartReadThread (&error) == false) + { + FILE *err_fh = GetErrorFileHandle(); + if (err_fh) + { + ::fprintf (err_fh, "error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); + exit(1); + } + } + +} + +FILE * +Debugger::GetInputFileHandle () +{ + return m_input_file.GetFileHandle(); +} + + +void +Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_output_file.SetFileHandle (fh, tranfer_ownership); + if (m_output_file.GetFileHandle() == NULL) + m_output_file.SetFileHandle (stdin, false); +} + +FILE * +Debugger::GetOutputFileHandle () +{ + return m_output_file.GetFileHandle(); +} + +void +Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_error_file.SetFileHandle (fh, tranfer_ownership); + if (m_error_file.GetFileHandle() == NULL) + m_error_file.SetFileHandle (stdin, false); +} + + +FILE * +Debugger::GetErrorFileHandle () +{ + return m_error_file.GetFileHandle(); +} + +CommandInterpreter & +Debugger::GetCommandInterpreter () +{ + return m_command_interpreter; +} + +Listener & +Debugger::GetListener () +{ + return m_listener; +} + + +TargetSP +Debugger::GetCurrentTarget () +{ + return m_target_list.GetCurrentTarget (); +} + +ExecutionContext +Debugger::GetCurrentExecutionContext () +{ + ExecutionContext exe_ctx; + exe_ctx.Clear(); + + lldb::TargetSP target_sp = GetCurrentTarget(); + exe_ctx.target = target_sp.get(); + + if (target_sp) + { + exe_ctx.process = target_sp->GetProcessSP().get(); + if (exe_ctx.process && exe_ctx.process->IsRunning() == false) + { + exe_ctx.thread = exe_ctx.process->GetThreadList().GetCurrentThread().get(); + if (exe_ctx.thread == NULL) + exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (exe_ctx.thread) + { + exe_ctx.frame = exe_ctx.thread->GetCurrentFrame().get(); + if (exe_ctx.frame == NULL) + exe_ctx.frame = exe_ctx.thread->GetStackFrameAtIndex (0).get(); + } + } + } + return exe_ctx; + +} + +SourceManager & +Debugger::GetSourceManager () +{ + return m_source_manager; +} + + +TargetList& +Debugger::GetTargetList () +{ + return m_target_list; +} + +void +Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) +{ + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); +} + + +void +Debugger::DispatchInput (const char *bytes, size_t bytes_len) +{ + if (bytes == NULL || bytes_len == 0) + return; + + // TODO: implement the STDIO to the process as an input reader... + TargetSP target = GetCurrentTarget(); + if (target.get() != NULL) + { + ProcessSP process_sp = target->GetProcessSP(); + if (process_sp.get() != NULL + && StateIsRunningState (process_sp->GetState())) + { + Error error; + if (process_sp->PutSTDIN (bytes, bytes_len, error) == bytes_len) + return; + } + } + + WriteToDefaultReader (bytes, bytes_len); +} + +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_readers.empty() && !m_input_reader_data.empty()) + { + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + + // Get the input reader from the top of the stack + InputReaderSP reader_sp(m_input_readers.top()); + + if (!reader_sp) + break; + + size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.data(), + 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 donesvn + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + +} + +void +Debugger::PushInputReader (const InputReaderSP& reader_sp) +{ + if (!reader_sp) + return; + if (!m_input_readers.empty()) + { + // Deactivate the old top reader + InputReaderSP top_reader_sp (m_input_readers.top()); + if (top_reader_sp) + top_reader_sp->Notify (eInputReaderDeactivate); + } + m_input_readers.push (reader_sp); + reader_sp->Notify (eInputReaderActivate); + ActivateInputReader (reader_sp); +} + +bool +Debugger::PopInputReader (const lldb::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_readers.empty()) + { + InputReaderSP reader_sp(m_input_readers.top()); + + if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) + { + m_input_readers.pop (); + reader_sp->Notify (eInputReaderDeactivate); + reader_sp->Notify (eInputReaderDone); + result = true; + + if (!m_input_readers.empty()) + { + reader_sp = m_input_readers.top(); + if (reader_sp) + { + ActivateInputReader (reader_sp); + reader_sp->Notify (eInputReaderReactivate); + } + } + } + } + return result; +} + +bool +Debugger::CheckIfTopInputReaderIsDone () +{ + bool result = false; + if (!m_input_readers.empty()) + { + InputReaderSP reader_sp(m_input_readers.top()); + + if (reader_sp && reader_sp->IsDone()) + { + result = true; + PopInputReader (reader_sp); + } + } + return result; +} + +void +Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +{ + FILE *in_fh = GetInputFileHandle(); + + if (in_fh) + { + struct termios in_fh_termios; + int in_fd = fileno (in_fh); + if (::tcgetattr(in_fd, &in_fh_termios) == 0) + { + if (reader_sp->GetEcho()) + in_fh_termios.c_lflag |= ECHO; // Turn on echoing + else + in_fh_termios.c_lflag &= ~ECHO; // Turn off echoing + + switch (reader_sp->GetGranularity()) + { + case eInputReaderGranularityByte: + case eInputReaderGranularityWord: + in_fh_termios.c_lflag &= ~ICANON; // Get one char at a time + break; + + case eInputReaderGranularityLine: + case eInputReaderGranularityAll: + in_fh_termios.c_lflag |= ICANON; // Get lines at a time + break; + + default: + break; + } + ::tcsetattr (in_fd, TCSANOW, &in_fh_termios); + } + } +} diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp new file mode 100644 index 000000000000..570ff7262258 --- /dev/null +++ b/lldb/source/Core/Disassembler.cpp @@ -0,0 +1,299 @@ +//===-- 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/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/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Timer.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; + + +Disassembler* +Disassembler::FindPlugin (const ArchSpec &arch) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "Disassembler::FindPlugin (arch = %s)", + arch.AsCString()); + + std::auto_ptr disassembler_ap; + DisassemblerCreateInstance create_callback; + for (uint32_t idx = 0; (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + disassembler_ap.reset (create_callback(arch)); + + if (disassembler_ap.get()) + return disassembler_ap.release(); + } + return NULL; +} + +bool +Disassembler::Disassemble +( + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t mixed_context_lines, + Stream &strm +) +{ + Disassembler *disassembler = Disassembler::FindPlugin(arch); + + if (disassembler) + { + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + size_t byte_size = 0; + if (exe_ctx.frame) + { + SymbolContext sc(exe_ctx.frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); + if (sc.function) + { + addr = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + byte_size = sc.function->GetAddressRange().GetByteSize(); + } + else if (sc.symbol && sc.symbol->GetAddressRangePtr()) + { + addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + { + byte_size = sc.symbol->GetAddressRangePtr()->GetByteSize(); + if (byte_size == 0) + byte_size = DEFAULT_DISASM_BYTE_SIZE; + } + } + else + { + addr = exe_ctx.frame->GetPC().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + byte_size = DEFAULT_DISASM_BYTE_SIZE; + } + } + + if (byte_size) + { + DataExtractor data; + size_t bytes_disassembled = disassembler->ParseInstructions (&exe_ctx, eAddressTypeLoad, addr, byte_size, data); + if (bytes_disassembled == 0) + { + return false; + } + else + { + // We got some things disassembled... + size_t num_instructions = disassembler->GetInstructionList().GetSize(); + uint32_t offset = 0; + SymbolContext sc; + SymbolContext prev_sc; + AddressRange sc_range; + if (mixed_context_lines) + strm.IndentMore (); + + for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i); + if (inst) + { + lldb::addr_t curr_addr = addr + offset; + if (mixed_context_lines) + { + if (!sc_range.ContainsLoadAddress (curr_addr, exe_ctx.process)) + { + prev_sc = sc; + Address curr_so_addr; + Process *process = exe_ctx.process; + if (process && process->ResolveLoadAddress (curr_addr, curr_so_addr)) + { + if (curr_so_addr.GetSection()) + { + Module *module = curr_so_addr.GetSection()->GetModule(); + uint32_t resolved_mask = module->ResolveSymbolContextForAddress(curr_so_addr, eSymbolContextEverything, sc); + if (resolved_mask) + { + sc.GetAddressRange (eSymbolContextEverything, sc_range); + if (sc != prev_sc) + { + if (offset != 0) + strm.EOL(); + + sc.DumpStopContext(&strm, process, curr_so_addr); + + if (sc.comp_unit && sc.line_entry.IsValid()) + { + Debugger::GetSharedInstance().GetSourceManager().DisplaySourceLinesWithLineNumbers ( + sc.line_entry.file, + sc.line_entry.line, + mixed_context_lines, + mixed_context_lines, + mixed_context_lines ? "->" : "", + &strm); + } + } + } + } + } + } + } + if (mixed_context_lines) + strm.IndentMore (); + strm.Indent(); + size_t inst_byte_size = inst->GetByteSize(); + //inst->Dump(&strm, curr_addr, &data, offset); // Do dump opcode bytes + inst->Dump(&strm, curr_addr, NULL, offset, exe_ctx, false); // Don't dump opcode bytes + strm.EOL(); + offset += inst_byte_size; + if (mixed_context_lines) + strm.IndentLess (); + } + else + { + break; + } + } + if (mixed_context_lines) + strm.IndentLess (); + + } + } + return true; + } + return false; +} + +Disassembler::Instruction::Instruction() +{ +} + +Disassembler::Instruction::~Instruction() +{ +} + + +Disassembler::InstructionList::InstructionList() : + m_instructions() +{ +} + +Disassembler::InstructionList::~InstructionList() +{ +} + +size_t +Disassembler::InstructionList::GetSize() const +{ + return m_instructions.size(); +} + + +Disassembler::Instruction * +Disassembler::InstructionList::GetInstructionAtIndex (uint32_t idx) +{ + if (idx < m_instructions.size()) + return m_instructions[idx].get(); + return NULL; +} + +const Disassembler::Instruction * +Disassembler::InstructionList::GetInstructionAtIndex (uint32_t idx) const +{ + if (idx < m_instructions.size()) + return m_instructions[idx].get(); + return NULL; +} + +void +Disassembler::InstructionList::Clear() +{ + m_instructions.clear(); +} + +void +Disassembler::InstructionList::AppendInstruction (Instruction::shared_ptr &inst_sp) +{ + if (inst_sp) + m_instructions.push_back(inst_sp); +} + + +size_t +Disassembler::ParseInstructions +( + const ExecutionContext *exe_ctx, + lldb::AddressType addr_type, + lldb::addr_t addr, + size_t byte_size, + DataExtractor& data +) +{ + Process *process = exe_ctx->process; + + if (process == NULL) + return 0; + + DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0')); + + Error error; + if (process->GetTarget().ReadMemory (addr_type, addr, data_sp->GetBytes(), data_sp->GetByteSize(), error, NULL)) + { + data.SetData(data_sp); + data.SetByteOrder(process->GetByteOrder()); + data.SetAddressByteSize(process->GetAddressByteSize()); + return ParseInstructions (data, 0, UINT32_MAX, addr); + } + + return 0; +} + +//---------------------------------------------------------------------- +// Disassembler copy constructor +//---------------------------------------------------------------------- +Disassembler::Disassembler(const ArchSpec& arch) : + m_arch (arch), + m_instruction_list(), + m_base_addr(LLDB_INVALID_ADDRESS) +{ + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Disassembler::~Disassembler() +{ +} + +Disassembler::InstructionList & +Disassembler::GetInstructionList () +{ + return m_instruction_list; +} + +const Disassembler::InstructionList & +Disassembler::GetInstructionList () const +{ + return m_instruction_list; +} diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp new file mode 100644 index 000000000000..b6b2f27caef5 --- /dev/null +++ b/lldb/source/Core/DynamicLoader.cpp @@ -0,0 +1,75 @@ +//===-- 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/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) + { + create_callback = PluginManager::GetDynamicLoaderCreateCallbackForPluginName (plugin_name); + if (create_callback) + { + std::auto_ptr instance_ap(create_callback(process)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::auto_ptr instance_ap(create_callback(process)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// DynamicLoader constructor +//---------------------------------------------------------------------- +DynamicLoader::DynamicLoader(Process *process) : + m_process (process), + m_stop_when_images_change(false) // Stop the process by default when a process' images change +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoader::~DynamicLoader() +{ +} + +//---------------------------------------------------------------------- +// Accessosors to the global setting as to wether to stop at image +// (shared library) loading/unloading. +//---------------------------------------------------------------------- +bool +DynamicLoader::GetStopWhenImagesChange () const +{ + return m_stop_when_images_change; +} + +void +DynamicLoader::SetStopWhenImagesChange (bool stop) +{ + m_stop_when_images_change = stop; +} + diff --git a/lldb/source/Core/Error.cpp b/lldb/source/Core/Error.cpp new file mode 100644 index 000000000000..c3522093f9b5 --- /dev/null +++ b/lldb/source/Core/Error.cpp @@ -0,0 +1,365 @@ +//===-- 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" + +#if defined (__arm__) +#include +#endif + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Error::Error(ValueType err, ErrorType type) : + m_code (err), + m_type (type), + m_string () +{ +} +//---------------------------------------------------------------------- +// 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 = (kern_return_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: + s = ::mach_error_string (m_code); + 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::SetError (kern_return_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; + m_string.append("\n"); + } + 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... + m_string.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); + int length = ::vsnprintf (&m_string[0], m_string.size(), format, args); + if (length < m_string.size()) + { + // The error formatted string fit into our buffer, just chop it down + // to size + m_string.erase (length); + } + else + { + // The error formatted string didn't fit into our buffer, resize it + // to the exact needed size, and retry + m_string.resize(length + 1); + length = ::vsnprintf (&m_string[0], m_string.size(), format, copy_args); + va_end (copy_args); + assert (length < m_string.size()); + } + 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; +} diff --git a/lldb/source/Core/Event.cpp b/lldb/source/Core/Event.cpp new file mode 100644 index 000000000000..c2bf08d8deb2 --- /dev/null +++ b/lldb/source/Core/Event.cpp @@ -0,0 +1,241 @@ +//===-- 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/Target/Process.h" + +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::Clear() +{ + m_data_ap.reset(); +} + +void +Event::Dump (Stream *s) const +{ + s->Printf("%p Event: broadcaster = %p, type = 0x%8.8x, data = ", this, m_broadcaster, m_type); + + if (m_data_ap.get() == NULL) + s->Printf (""); + else + { + s->PutChar('{'); + m_data_ap->Dump (s); + s->PutChar('}'); + } +} + +Broadcaster * +Event::GetBroadcaster () const +{ + return m_broadcaster; +} + +bool +Event::BroadcasterIs (Broadcaster *broadcaster) +{ + return broadcaster == m_broadcaster; +} + +uint32_t +Event::GetType() const +{ + return m_type; +} + + +EventData * +Event::GetData () +{ + return m_data_ap.get(); +} + +const EventData * +Event::GetData () const +{ + return m_data_ap.get(); +} + +void +Event::DoOnRemoval () +{ + if (m_data_ap.get()) + m_data_ap->DoOnRemoval (this); +} + +void +Event::SetBroadcaster (Broadcaster *broadcaster) +{ + m_broadcaster = broadcaster; +} + +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 + { + DataExtractor data; + data.SetData(m_bytes.data(), m_bytes.size(), eByteOrderHost); + 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.data(); +} + +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; +} + diff --git a/lldb/source/Core/FileSpec.cpp b/lldb/source/Core/FileSpec.cpp new file mode 100644 index 000000000000..305650d86439 --- /dev/null +++ b/lldb/source/Core/FileSpec.cpp @@ -0,0 +1,580 @@ +//===-- 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 + +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +static bool +GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) +{ + char resolved_path[PATH_MAX]; + if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path))) + return ::stat (resolved_path, stats_ptr) == 0; + return false; +} + +static const char* +GetCachedGlobTildeSlash() +{ + static std::string g_tilde; + if (g_tilde.empty()) + { + glob_t globbuf; + if (::glob("~/", GLOB_TILDE, NULL, &globbuf) == 0) //success + { + g_tilde = globbuf.gl_pathv[0]; + ::globfree (&globbuf); + } + if (g_tilde.empty()) + return NULL; + } + return g_tilde.c_str(); +} + +int +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]; + if (::strstr (src_path, "~/") == src_path) + ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s%s", GetCachedGlobTildeSlash(), src_path + 2); + else + ::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) : + m_directory(), + m_filename() +{ + if (pathname && pathname[0]) + SetFile(pathname); +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec& rhs) : + m_directory (rhs.m_directory), + m_filename (rhs.m_filename) +{ +} + +//------------------------------------------------------------------ +// 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; + } + 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) +{ + m_filename.Clear(); + m_directory.Clear(); + if (pathname == NULL || pathname[0] == '\0') + return; + + char resolved_path[PATH_MAX]; + + if (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1) + { + 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 +void*() const +{ + return (m_directory || m_filename) ? const_cast(this) : NULL; +} + +//---------------------------------------------------------------------- +// 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 +{ + return m_directory == rhs.m_directory && m_filename == rhs.m_filename; +} + +//------------------------------------------------------------------ +// Not equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator!= (const FileSpec& rhs) const +{ + return m_filename != rhs.m_filename || m_directory != rhs.m_directory; +} + +//------------------------------------------------------------------ +// 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) + return a == b; + else + return a.m_filename == b.m_filename; +} + + + +//------------------------------------------------------------------ +// Dump the object to the supplied stream. If the object contains +// a valid directory name, it will be displayed followed by a +// directory delimiter, and the filename. +//------------------------------------------------------------------ +void +FileSpec::Dump(Stream *s) const +{ + if (m_filename) + m_directory.Dump(s, ""); // Provide a default for m_directory when we dump it in case it is invalid + + if (m_directory) + { + // If dirname was valid, then we need to print a slash between + // the directory and the filename + 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); +} + +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 eFileTypeUknown; + } + return eFileTypeInvalid; +} + +TimeValue +FileSpec::GetModificationTime () const +{ + TimeValue mod_time; + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + mod_time = file_stats.st_mtimespec; + 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. +//------------------------------------------------------------------ +bool +FileSpec::GetPath(char *path, size_t max_path_length) const +{ + if (max_path_length == 0) + return false; + + path[0] = '\0'; + const char *dirname = m_directory.AsCString(); + const char *filename = m_filename.AsCString(); + if (dirname) + { + if (filename && filename[0]) + { + return snprintf (path, max_path_length, "%s/%s", dirname, filename) < max_path_length; + } + else + { + strncpy (path, dirname, max_path_length); + } + } + else if (filename) + { + strncpy (path, filename, max_path_length); + } + + // Any code paths that reach here assume that strncpy, or a similar function was called + // where any remaining bytes will be filled with NULLs and that the string won't be + // NULL terminated if it won't fit in the buffer. + + // If the last character is NULL, then all went well + if (path[max_path_length-1] == '\0') + return true; + + // Make sure the path is terminated, as it didn't fit into "path" + path[max_path_length-1] = '\0'; + return false; +} + +//------------------------------------------------------------------ +// 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; + auto_ptr mmap_data(new DataBufferMemoryMap()); + if (mmap_data.get()) + { + if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= 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(); +} + +//------------------------------------------------------------------ +// 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) const +{ + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + int fd = ::open (resolved_path, O_RDONLY, 0); + if (fd != -1) + { + struct stat file_stats; + if (::fstat (fd, &file_stats) == 0) + { + // Read bytes directly into our basic_string buffer + if (file_stats.st_size > 0) + { + off_t lseek_result = 0; + if (file_offset > 0) + lseek_result = ::lseek (fd, file_offset, SEEK_SET); + + if (lseek_result < 0) + { + // Get error from errno + } + else if (lseek_result == file_offset) + { + std::auto_ptr data_heap_ap; + if (file_stats.st_size < file_size) + data_heap_ap.reset(new DataBufferHeap(file_stats.st_size, '\0')); + else + data_heap_ap.reset(new DataBufferHeap(file_size, '\0')); + + if (data_heap_ap.get()) + { + ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize()); + if (bytesRead >= 0) + { + // Make sure we read exactly what we asked for and if we got + // less, adjust the array + if (bytesRead < data_heap_ap->GetByteSize()) + data_heap_ap->SetByteSize(bytesRead); + data_sp.reset(data_heap_ap.release()); + } + } + } + } + } + } + close(fd); + } + return data_sp; +} + +bool +FileSpec::ReadFileLines (STLStringArray &lines) +{ + bool ret_val = false; + lines.clear(); + + std::string dir_str (m_directory.AsCString()); + std::string file_str (m_filename.AsCString()); + std::string full_name = dir_str + "/" + file_str; + + ifstream file_stream (full_name.c_str()); + + if (file_stream) + { + std::string line; + while (getline (file_stream, line)) + lines.push_back (line); + ret_val = true; + } + + return ret_val; +} diff --git a/lldb/source/Core/FileSpecList.cpp b/lldb/source/Core/FileSpecList.cpp new file mode 100644 index 000000000000..17abf4b24e53 --- /dev/null +++ b/lldb/source/Core/FileSpecList.cpp @@ -0,0 +1,228 @@ +//===-- 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" + +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 +{ + for_each (m_files.begin(), m_files.end(), bind2nd(mem_fun_ref(&FileSpec::Dump),s)); +} + +//------------------------------------------------------------------ +// 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. +//------------------------------------------------------------------ +uint32_t +FileSpecList::FindFileIndex (uint32_t start_idx, const FileSpec &file_spec) const +{ + const uint32_t num_files = m_files.size(); + uint32_t idx; + + // 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 (idx = start_idx; idx < num_files; ++idx) + { + if (compare_filename_only) + { + if (m_files[idx].GetFilename() == file_spec.GetFilename()) + return idx; + } + else + { + if (m_files[idx] == file_spec) + 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(uint32_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(uint32_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. +//------------------------------------------------------------------ +uint32_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/lldb/source/Core/Flags.cpp b/lldb/source/Core/Flags.cpp new file mode 100644 index 000000000000..13cbd85915b5 --- /dev/null +++ b/lldb/source/Core/Flags.cpp @@ -0,0 +1,122 @@ +//===-- Flags.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/Flags.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +Flags::Flags (ValueType flags) : + m_flags(flags) +{ +} + +//---------------------------------------------------------------------- +// Copy Constructor +//---------------------------------------------------------------------- +Flags::Flags (const Flags& rhs) : + m_flags(rhs.m_flags) +{ +} + +//---------------------------------------------------------------------- +// Virtual destructor in case anyone inherits from this class. +//---------------------------------------------------------------------- +Flags::~Flags () +{ +} + +//---------------------------------------------------------------------- +// Get accessor for all of the current flag bits. +//---------------------------------------------------------------------- +Flags::ValueType +Flags::GetAllFlagBits () const +{ + return m_flags; +} + +size_t +Flags::GetBitSize() const +{ + return sizeof (ValueType) * 8; +} + +//---------------------------------------------------------------------- +// Set accessor for all of the current flag bits. +//---------------------------------------------------------------------- +void +Flags::SetAllFlagBits (ValueType flags) +{ + m_flags = flags; +} + +//---------------------------------------------------------------------- +// Clear one or more bits in our flag bits +//---------------------------------------------------------------------- +Flags::ValueType +Flags::Clear (ValueType bits) +{ + m_flags &= ~bits; + return m_flags; +} + +//---------------------------------------------------------------------- +// Set one or more bits in our flag bits +//---------------------------------------------------------------------- +Flags::ValueType +Flags::Set (ValueType bits) +{ + m_flags |= bits; + return m_flags; +} + +//---------------------------------------------------------------------- +// Returns true if any flag bits in "bits" are set +//---------------------------------------------------------------------- +bool +Flags::IsSet (ValueType bits) const +{ + return (m_flags & bits) != 0; +} + +//---------------------------------------------------------------------- +// Returns true if all flag bits in "bits" are clear +//---------------------------------------------------------------------- +bool +Flags::IsClear (ValueType bits) const +{ + return (m_flags & bits) == 0; +} + + +size_t +Flags::SetCount () const +{ + size_t count = 0; + for (ValueType mask = m_flags; mask; mask >>= 1) + { + if (mask & 1) + ++count; + } + return count; +} + +size_t +Flags::ClearCount () const +{ + size_t count = 0; + for (ValueType shift = 0; shift < sizeof(ValueType)*8; ++shift) + { + if ((m_flags & (1u << shift)) == 0) + ++count; + } + return count; +} diff --git a/lldb/source/Core/InputReader.cpp b/lldb/source/Core/InputReader.cpp new file mode 100644 index 000000000000..c139a87387a6 --- /dev/null +++ b/lldb/source/Core/InputReader.cpp @@ -0,0 +1,343 @@ +//===-- 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 + +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Debugger.h" + +using namespace lldb; +using namespace lldb_private; + +InputReader::InputReader () : + m_callback (NULL), + m_callback_baton (NULL), + m_end_token (), + m_granularity (eInputReaderGranularityInvalid), + m_done (true), + m_echo (true), + m_active (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) + { + 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; + const char *end = bytes + bytes_len; + 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; +} + + +FILE * +InputReader::GetInputFileHandle () +{ + return Debugger::GetSharedInstance().GetInputFileHandle (); +} + +FILE * +InputReader::GetOutputFileHandle () +{ + return Debugger::GetSharedInstance().GetOutputFileHandle (); +} + +const char * +InputReader::GetPrompt () const +{ + if (!m_prompt.empty()) + return m_prompt.c_str(); + else + return NULL; +} + +void +InputReader::RefreshPrompt () +{ + if (!m_prompt.empty()) + { + FILE *out_fh = GetOutputFileHandle(); + if (out_fh) + ::fprintf (out_fh, "%s", m_prompt.c_str()); + } +} + +void +InputReader::Notify (InputReaderAction notification) +{ + switch (notification) + { + case eInputReaderActivate: + case eInputReaderReactivate: + m_active = true; + break; + + case eInputReaderDeactivate: + case eInputReaderDone: + m_active = false; + 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); +} diff --git a/lldb/source/Core/Language.cpp b/lldb/source/Core/Language.cpp new file mode 100644 index 000000000000..82b4e4f3acad --- /dev/null +++ b/lldb/source/Core/Language.cpp @@ -0,0 +1,150 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +#define ENUM_TO_DCSTREAM(x) case x: s->PutCString(#x); return + +typedef 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 uint32_t +g_num_languages = sizeof(g_languages)/sizeof(LanguageStrings); + +Language::Language(Language::Type language) : + m_language (language) +{ +} + +Language::~Language() +{ +} + +Language::Type +Language::GetLanguage() const +{ + return m_language; +} + +void +Language::Clear () +{ + m_language = Unknown; +} + +void +Language::SetLanguage(Language::Type 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/lldb/source/Core/Listener.cpp b/lldb/source/Core/Listener.cpp new file mode 100644 index 000000000000..639b74a4c7d1 --- /dev/null +++ b/lldb/source/Core/Listener.cpp @@ -0,0 +1,480 @@ +//===-- 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" + +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); + 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(); +} + +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::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 (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.Reset(); + 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) + { + 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 + m_cond_wait.SetValue (false, eBroadcastNever); + + if (m_cond_wait.WaitForValueEqualTo (true, timeout, &timed_out)) + continue; + + else if (timed_out) + { + if (log) + log->Printf ("%p Listener::WaitForEvents() timed out for %s", this, m_name.c_str()); + break; + } + else + { + if (log) + log->Printf ("%p Listener::WaitForEvents() 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; +} diff --git a/lldb/source/Core/Log.cpp b/lldb/source/Core/Log.cpp new file mode 100644 index 000000000000..fe2071683cac --- /dev/null +++ b/lldb/source/Core/Log.cpp @@ -0,0 +1,590 @@ +//===-- Log.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 + +// 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" + +using namespace lldb; +using namespace lldb_private; + +static Stream * +StreamForSTDOUTAccess (bool set, StreamSP &stream_sp) +{ + // Since we are in a shared library and we can't have global + // constructors, we need to control access to this static variable + // through an accessor function to get and set the value. + static StreamSP g_stream_sp; + + if (set) + g_stream_sp = stream_sp; + else + { + if (g_stream_sp) + stream_sp = g_stream_sp; + else + { + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + if (out_fh) + stream_sp.reset(new StreamFile(out_fh)); + else + stream_sp.reset(); + } + } + return stream_sp.get(); +} + +StreamSP +Log::GetStreamForSTDOUT () +{ + StreamSP stream_sp; + StreamForSTDOUTAccess (false, stream_sp); + return stream_sp; +} + +void +Log::SetStreamForSTDOUT (StreamSP &stream_sp) +{ + StreamForSTDOUTAccess (true, stream_sp); +} + +void +Log::STDOUT (const char *format, ...) +{ + StreamSP stream_sp; + if (StreamForSTDOUTAccess(false, stream_sp)) + { + va_list args; + va_start (args, format); + stream_sp->PrintfVarArg(format, args); + va_end (args); + } +} + +static Stream * +StreamForSTDERRAccess (bool set, StreamSP &stream_sp) +{ + // Since we are in a shared library and we can't have global + // constructors, we need to control access to this static variable + // through an accessor function to get and set the value. + static StreamSP g_stream_sp(new StreamFile(Debugger::GetSharedInstance().GetErrorFileHandle())); + + if (set) + g_stream_sp = stream_sp; + else + stream_sp = g_stream_sp; + return stream_sp.get(); +} + +StreamSP +Log::GetStreamForSTDERR () +{ + StreamSP stream_sp; + StreamForSTDERRAccess (false, stream_sp); + return stream_sp; +} + +void +Log::SetStreamForSTDERR (StreamSP &stream_sp) +{ + StreamForSTDERRAccess (true, stream_sp); +} + +void +Log::STDERR (const char *format, ...) +{ + StreamSP stream_sp; + if (StreamForSTDERRAccess(false, stream_sp)) + { + va_list args; + va_start (args, format); + stream_sp->PrintfVarArg(format, args); + va_end (args); + } +} + +Log::Log () : + m_stream_sp(), + m_options(0), + m_mask_bits(0) +{ +} + +Log::Log (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; + static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); + + Mutex::Locker locker; + + uint32_t log_options = m_options.GetAllFlagBits(); + + // Lock the threaded logging mutex if we are doing thread safe logging + if (log_options & LLDB_LOG_OPTION_THREADSAFE) + locker.Reset(g_LogThreadedMutex.GetMutex()); + + // Add a sequence ID if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_SEQUENCE) + header.Printf ("%u ", ++g_sequence_id); + + // Timestamp if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_TIMESTAMP) + { + struct timeval tv = TimeValue::Now().GetAsTimeVal(); + header.Printf ("%9llu.%6.6llu ", tv.tv_sec, tv.tv_usec); + } + + // Add the process and thread if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD) + header.Printf ("[%4.4x/%4.4x]: ", getpid(), mach_thread_self()); + + // Add the process and thread if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_THREAD_NAME) + { + const char *thread_name_str = Host::GetThreadName (getpid(), mach_thread_self()); + if (thread_name_str) + header.Printf ("%s ", thread_name_str); + } + + header.PrintfVarArg (format, args); + m_stream_sp->Printf("%s\n", header.GetData()); + } +} + + +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().IsSet(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().IsSet(LLDB_LOG_OPTION_DEBUG) && GetOptions().IsSet(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 ((bits & m_options.GetAllFlagBits()) == 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.IsSet(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.IsSet(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 char *channel, const Log::Callbacks &log_callbacks) +{ + GetCallbackMap().insert(std::make_pair(channel, log_callbacks)); +} + +bool +Log::UnregisterLogChannel (const char *channel) +{ + return GetCallbackMap().erase(channel) != 0; +} + +bool +Log::GetLogChannelCallbacks (const char *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; + } + ::bzero (&log_callbacks, sizeof(log_callbacks)); + return false; +} + +void +Log::EnableAllLogChannels +( + StreamSP &log_stream_sp, + uint32_t log_options, + Args &args, + 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, args, 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, args); + } + +} + +void +Log::DisableAllLogChannels () +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.disable (); + + 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 (); +} + +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 +{ + 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()) + { + LogChannelCreateInstance create_callback = PluginManager::GetLogChannelCreateCallbackForPluginName (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_sp () +{ +} + +LogChannel::~LogChannel () +{ +} + +const char * +LogChannel::GetPluginSuffix () +{ + return ".log-channel"; +} + + diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp new file mode 100644 index 000000000000..38667a86def1 --- /dev/null +++ b/lldb/source/Core/Mangled.cpp @@ -0,0 +1,733 @@ +//===-- Mangled.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/Core/ConstString.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +using namespace lldb_private; + +#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 char *s, bool mangled) : + m_mangled(), + m_demangled() +{ + if (s && s[0]) + { + SetValue(s, mangled); + } +} + +//---------------------------------------------------------------------- +// 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(), a.GetName()); +} + + + +//---------------------------------------------------------------------- +// 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 char *s, bool mangled) +{ + m_mangled.Clear(); + m_demangled.Clear(); + + if (s) + { + if (mangled) + m_mangled.SetCString (s); + else + m_demangled.SetCString(s); + } +} + + +//---------------------------------------------------------------------- +// 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()); + + // We already know mangled is valid from the above check, + // lets just make sure it isn't empty... + const char * mangled = m_mangled.AsCString(); + if (mangled[0]) + { + // The first time the demangling routine is called, it will + // return a buffer value and length and we will continue to + // re-use that buffer so we don't always have to malloc/free + // a buffer for each demangle. The buffer can be realloc'ed + // by abi::__cxa_demangle, so we may need to make it thread + // specific if we ever start doing multi-threaded calls to + // this function. g_demangle_buf will currently leak one + // malloc entry that can vary in size. If we need to reclaim + // this memory, we will need to add some code to free this + // buffer at exit time. + static char *g_demangle_buf = NULL; + static size_t g_demangle_buf_len = 0; + int status = 0; + g_demangle_buf = abi::__cxa_demangle(mangled, g_demangle_buf, &g_demangle_buf_len, &status); + if (g_demangle_buf != NULL) + { + m_demangled.SetCString(g_demangle_buf); + } + else + { + // Set the demangled string to the empty string to indicate we + // tried to parse it once and failed. + m_demangled.SetCString(""); + } + } + } + + return m_demangled; +} + +//---------------------------------------------------------------------- +// Mangled name get accessor +//---------------------------------------------------------------------- +ConstString& +Mangled::GetMangledName () +{ + return m_mangled; +} + +//---------------------------------------------------------------------- +// Mangled name const get accessor +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetMangledName () const +{ + return m_mangled; +} + +//---------------------------------------------------------------------- +// Get the demangled name if there is one, else return the mangled name. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetName () const +{ + const ConstString& name = GetDemangledName(); + if (name && !name.IsEmpty()) + return name; + return m_mangled; +} + +//---------------------------------------------------------------------- +// Generate the tokens from the demangled name. +// +// Returns the number of tokens that were parsed. +//---------------------------------------------------------------------- +size_t +Mangled::GetTokens (Mangled::TokenList &tokens) const +{ + tokens.Clear(); + const ConstString& demangled = GetDemangledName(); + if (demangled && !demangled.IsEmpty()) + tokens.Parse(demangled.AsCString()); + + return tokens.Size(); +} + +//---------------------------------------------------------------------- +// 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; +} + + + + +#pragma mark Mangled::Token + +//-------------------------------------------------------------- +// Default constructor +//-------------------------------------------------------------- +Mangled::Token::Token () : + type(eInvalid), + value() +{ +} + +//-------------------------------------------------------------- +// Equal to operator +//-------------------------------------------------------------- +bool +Mangled::Token::operator== (const Token& rhs) const +{ + return type == rhs.type && value == rhs.value; +} + +//-------------------------------------------------------------- +// Dump the token to a stream "s" +//-------------------------------------------------------------- +void +Mangled::Token::Dump (Stream *s) const +{ + switch (type) + { + case eInvalid: s->PutCString("invalid "); break; + case eNameSpace: s->PutCString("namespace "); break; + case eMethodName: s->PutCString("method "); break; + case eType: s->PutCString("type "); break; + case eTemplate: s->PutCString("template "); break; + case eTemplateBeg: s->PutCString("template < "); break; + case eTemplateEnd: s->PutCString("template > "); break; + case eParamsBeg: s->PutCString("params ( "); break; + case eParamsEnd: s->PutCString("params ) "); break; + case eQualifier: s->PutCString("qualifier "); break; + case eError: s->PutCString("ERROR "); break; + default: + s->Printf("type = %i", type); + break; + } + value.DumpDebug(s); +} + +//-------------------------------------------------------------- +// Returns true if this token is a wildcard +//-------------------------------------------------------------- +bool +Mangled::Token::IsWildcard () const +{ + static ConstString g_wildcard_str("*"); + return value == g_wildcard_str; +} + + +//---------------------------------------------------------------------- +// Dump "obj" to the supplied stream "s" +//---------------------------------------------------------------------- +Stream& +lldb_private::operator << (Stream& s, const Mangled::Token& obj) +{ + obj.Dump(&s); + return s; +} + + +#pragma mark Mangled::TokenList +//---------------------------------------------------------------------- +// Mangled::TokenList +//---------------------------------------------------------------------- + +//-------------------------------------------------------------- +// Default constructor. If demangled is non-NULL and not-empty +// the token list will parse up the demangled string it is +// given, else the object will initialize an empty token list. +//-------------------------------------------------------------- +Mangled::TokenList::TokenList (const char *demangled) : + m_tokens() +{ + if (demangled && demangled[0]) + { + Parse(demangled); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Mangled::TokenList::~TokenList () +{ +} + +//---------------------------------------------------------------------- +// Parses "demangled" into tokens. This allows complex +// comparisons to be done. Comparisons can include wildcards at +// the namespace, method name, template, and template and +// parameter type levels. +// +// Example queries include: +// "std::basic_string<*>" // Find all std::basic_string variants +// "std::basic_string<*>::erase(*)" // Find all std::basic_string::erase variants with any number of parameters +// "*::clear()" // Find all functions with a method name of +// // "clear" that are in any namespace that +// // have no parameters +// "::printf" // Find the printf function in the global namespace +// "printf" // Ditto +// "foo::*(int)" // Find all functions in the class or namespace "foo" that take a single integer argument +// +// Returns the number of tokens that were decoded, or zero when +// we fail. +//---------------------------------------------------------------------- +size_t +Mangled::TokenList::Parse (const char *s) +{ + m_tokens.clear(); + + Token token; + token.type = eNameSpace; + + TokenType max_type = eInvalid; + const char *p = s; + size_t span = 0; + size_t sep_size = 0; + + while (*p != '\0') + { + p = p + span + sep_size; + while (isspace(*p)) + ++p; + + if (*p == '\0') + break; + + span = strcspn(p, ":<>(),"); + sep_size = 1; + token.type = eInvalid; + switch (p[span]) + { + case '\0': + break; + + case ':': + if (p[span+1] == ':') + { + sep_size = 2; + if (span > 0) + { + token.type = eNameSpace; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + else + continue; + } + break; + + case '(': + if (span > 0) + { + token.type = eMethodName; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eParamsBeg; + token.value.Clear(); + m_tokens.push_back(token); + break; + + case ',': + if (span > 0) + { + token.type = eType; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + else + { + continue; + } + break; + + case ')': + if (span > 0) + { + token.type = eType; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eParamsEnd; + token.value.Clear(); + m_tokens.push_back(token); + break; + + case '<': + if (span > 0) + { + token.type = eTemplate; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eTemplateBeg; + token.value.Clear(); + m_tokens.push_back(token); + break; + + case '>': + if (span > 0) + { + token.type = eType; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eTemplateEnd; + token.value.Clear(); + m_tokens.push_back(token); + break; + } + + if (max_type < token.type) + max_type = token.type; + + if (token.type == eInvalid) + { + if (max_type >= eParamsEnd) + { + token.type = eQualifier; + token.value.SetCString(p); + m_tokens.push_back(token); + } + else if (max_type >= eParamsBeg) + { + token.type = eType; + token.value.SetCString(p); + m_tokens.push_back(token); + } + else + { + token.type = eMethodName; + token.value.SetCString(p); + m_tokens.push_back(token); + } + break; + } + } + return m_tokens.size(); +} + + +//---------------------------------------------------------------------- +// Clear the token list. +//---------------------------------------------------------------------- +void +Mangled::TokenList::Clear () +{ + m_tokens.clear(); +} + +//---------------------------------------------------------------------- +// Dump the token list to the stream "s" +//---------------------------------------------------------------------- +void +Mangled::TokenList::Dump (Stream *s) const +{ + collection::const_iterator pos; + collection::const_iterator beg = m_tokens.begin(); + collection::const_iterator end = m_tokens.end(); + for (pos = beg; pos != end; ++pos) + { + s->Indent("token["); + *s << (uint32_t)std::distance(beg, pos) << "] = " << *pos << "\n"; + } +} + +//---------------------------------------------------------------------- +// Find the first token in the list that has "token_type" as its +// type +//---------------------------------------------------------------------- +const Mangled::Token * +Mangled::TokenList::Find (TokenType token_type) const +{ + collection::const_iterator pos; + collection::const_iterator beg = m_tokens.begin(); + collection::const_iterator end = m_tokens.end(); + for (pos = beg; pos != end; ++pos) + { + if (pos->type == token_type) + return &(*pos); + } + return NULL; +} + +//---------------------------------------------------------------------- +// Return the token at index "idx", or NULL if the index is +// out of range. +//---------------------------------------------------------------------- +const Mangled::Token * +Mangled::TokenList::GetTokenAtIndex (uint32_t idx) const +{ + if (idx < m_tokens.size()) + return &m_tokens[idx]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Given a token list, see if it matches this object's tokens. +// "token_list" can contain wild card values to enable powerful +// matching. Matching the std::string::erase(*) example that was +// tokenized above we could use a token list such as: +// +// token name +// ----------- ---------------------------------------- +// eNameSpace "std" +// eTemplate "basic_string" +// eTemplateBeg +// eInvalid "*" +// eTemplateEnd +// eMethodName "erase" +// eParamsBeg +// eInvalid "*" +// eParamsEnd +// +// Returns true if it "token_list" matches this object's tokens, +// false otherwise. +//---------------------------------------------------------------------- +bool +Mangled::TokenList::MatchesQuery (const Mangled::TokenList &match) const +{ + size_t match_count = 0; + collection::const_iterator pos; + collection::const_iterator pos_end = m_tokens.end(); + + collection::const_iterator match_pos; + collection::const_iterator match_pos_end = match.m_tokens.end(); + collection::const_iterator match_wildcard_pos = match_pos_end; + collection::const_iterator match_next_pos = match_pos_end; + + size_t template_scope_depth = 0; + + for (pos = m_tokens.begin(), match_pos = match.m_tokens.begin(); + pos != pos_end && match_pos != match_pos_end; + ++match_pos) + { + match_next_pos = match_pos + 1; + // Is this a wildcard? + if (match_pos->IsWildcard()) + { + if (match_wildcard_pos != match_pos_end) + return false; // Can't have two wildcards in effect at once. + + match_wildcard_pos = match_pos; + // Are we at the end of the MATCH token list? + if (match_next_pos == match_pos_end) + { + // There is nothing more to match, return if we have any matches so far... + return match_count > 0; + } + } + + if (match_pos->type == eInvalid || match_pos->type == eError) + { + return false; + } + else + { + if (match_pos->type == eTemplateBeg) + { + ++template_scope_depth; + } + else if (match_pos->type == eTemplateEnd) + { + assert(template_scope_depth > 0); + --template_scope_depth; + } + + // Do we have a wildcard going right now? + if (match_wildcard_pos == match_pos_end) + { + // No wildcard matching right now, just check and see if things match + if (*pos == *match_pos) + ++match_count; + else + return false; + } + else + { + // We have a wildcard match going + + // For template types we need to make sure to match the template depths... + const size_t start_wildcard_template_scope_depth = template_scope_depth; + size_t curr_wildcard_template_scope_depth = template_scope_depth; + while (pos != pos_end) + { + if (match_wildcard_pos->type == eNameSpace && pos->type == eParamsBeg) + return false; + + if (start_wildcard_template_scope_depth == curr_wildcard_template_scope_depth) + { + if (*pos == *match_next_pos) + { + ++match_count; + match_pos = match_next_pos; + match_wildcard_pos = match_pos_end; + break; + } + } + if (pos->type == eTemplateBeg) + ++curr_wildcard_template_scope_depth; + else if (pos->type == eTemplateEnd) + --curr_wildcard_template_scope_depth; + + + ++pos; + } + } + } + + if (pos != pos_end) + ++pos; + } + if (match_pos != match_pos_end) + return false; + + return match_count > 0; +} + + +//---------------------------------------------------------------------- +// Return the number of tokens in the token collection +//---------------------------------------------------------------------- +size_t +Mangled::TokenList::Size () const +{ + return m_tokens.size(); +} + + +//---------------------------------------------------------------------- +// Stream out the tokens +//---------------------------------------------------------------------- +Stream& +lldb_private::operator << (Stream& s, const Mangled::TokenList& obj) +{ + obj.Dump(&s); + return s; +} diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp new file mode 100644 index 000000000000..c8e7d2f2c42f --- /dev/null +++ b/lldb/source/Core/Module.cpp @@ -0,0 +1,515 @@ +//===-- 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/Core/Module.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" + +using namespace lldb; +using namespace lldb_private; + +Module::Module(const FileSpec& file_spec, const ArchSpec& arch, const ConstString *object_name, off_t object_offset) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (file_spec.GetModificationTime()), + m_arch (arch), + m_uuid (), + m_file (file_spec), + m_flags (), + m_object_name (), + m_objfile_ap (), + m_symfile_ap () +{ + if (object_name) + m_object_name = *object_name; + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Module::Module((%s) '%s/%s%s%s%s')", + this, + m_arch.AsCString(), + m_file.GetDirectory().AsCString(""), + m_file.GetFilename().AsCString(""), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); + +} + +Module::~Module() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Module::~Module((%s) '%s/%s%s%s%s')", + this, + m_arch.AsCString(), + m_file.GetDirectory().AsCString(""), + m_file.GetFilename().AsCString(""), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); +} + + +ModuleSP +Module::GetSP () +{ + return ModuleList::GetModuleSP (this); +} + +const UUID& +Module::GetUUID() +{ + Mutex::Locker locker (m_mutex); + if (m_flags.IsClear(flagsParsedUUID)) + { + ObjectFile * obj_file = GetObjectFile (); + + if (obj_file != NULL) + { + obj_file->GetUUID(&m_uuid); + m_flags.Set(flagsParsedUUID); + } + } + return m_uuid; +} + +void +Module::ParseAllDebugSymbols() +{ + Mutex::Locker locker (m_mutex); + uint32_t num_comp_units = GetNumCompileUnits(); + if (num_comp_units == 0) + return; + + TargetSP null_target; + SymbolContext sc(null_target, GetSP()); + uint32_t cu_idx; + SymbolVendor *symbols = GetSymbolVendor (); + + for (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); + + uint32_t func_idx; + for (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 = GetSP(); +} + +void +Module::DumpSymbolContext(Stream *s) +{ + s->Printf(", Module{0x%8.8x}", this); +} + +uint32_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 (uint32_t index) +{ + Mutex::Locker locker (m_mutex); + uint32_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; +} + +//CompUnitSP +//Module::FindCompUnit(lldb::user_id_t uid) +//{ +// CompUnitSP cu_sp; +// SymbolVendor *symbols = GetSymbolVendor (); +// if (symbols) +// cu_sp = symbols->FindCompUnit(uid); +// 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%llx)", vm_addr); + ObjectFile* ofile = GetObjectFile(); + if (ofile) + return so_addr.ResolveAddressUsingFileSections(vm_addr, ofile->GetSectionList()); + 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 + sc.Clear(); + + // Get the section from the section/offset address. + const Section *section = so_addr.GetSection(); + + // Make sure the section matches this module before we try and match anything + if (section && section->GetModule() == this) + { + // If the section offset based address resolved itself, then this + // is the right module. + sc.module_sp = GetSP(); + 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 + if (resolve_scope & eSymbolContextSymbol) + { + ObjectFile* ofile = GetObjectFile(); + if (ofile) + { + Symtab *symtab = ofile->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); + 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%s%s:%u, check_inlines = %s, resolve_scope = 0x%8.8x)", + file_spec.GetDirectory().AsCString(""), + file_spec.GetDirectory() ? "/" : "", + file_spec.GetFilename().AsCString(""), + 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; +} + + +uint32_t +Module::FindGlobalVariables(const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(name, append, max_matches, variables); + return 0; +} +uint32_t +Module::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(regex, append, max_matches, variables); + return 0; +} + +uint32_t +Module::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindFunctions(name, append, sc_list); + return 0; +} + +uint32_t +Module::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindFunctions(regex, append, sc_list); + return 0; +} + +//uint32_t +//Module::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& types) +//{ +// Timer scoped_timer(__PRETTY_FUNCTION__); +// SymbolVendor *symbols = GetSymbolVendor (); +// if (symbols) +// return symbols->FindTypes(sc, name, append, max_matches, encoding, udt_name, types); +// return 0; +//} +// +//uint32_t +//Module::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& types) +//{ +// Timer scoped_timer(__PRETTY_FUNCTION__); +// SymbolVendor *symbols = GetSymbolVendor (); +// if (symbols) +// return symbols->FindTypes(sc, regex, append, max_matches, encoding, udt_name, types); +// return 0; +// +//} + +SymbolVendor* +Module::GetSymbolVendor (bool can_create) +{ + Mutex::Locker locker (m_mutex); + if (m_flags.IsClear(flagsSearchedForSymVendor) && can_create) + { + ObjectFile *obj_file = GetObjectFile (); + if (obj_file != NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + m_symfile_ap.reset(SymbolVendor::FindPlugin(this)); + m_flags.Set (flagsSearchedForSymVendor); + } + } + return m_symfile_ap.get(); +} + +const FileSpec & +Module::GetFileSpec () const +{ + return m_file; +} + +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; +} + +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%s\n", + m_file.GetDirectory().AsCString(), + m_file.GetFilename().AsCString(), + 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_flags.IsClear(flagsSearchedForObjParser)) + { + m_flags.Set (flagsSearchedForObjParser); + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); + m_objfile_ap.reset(ObjectFile::FindPlugin(this, &m_file, 0, m_file.GetByteSize())); + } + return m_objfile_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); + ObjectFile *objfile = GetObjectFile(); + if (objfile) + { + Symtab *symtab = objfile->GetSymtab(); + if (symtab) + return symtab->FindFirstSymbolWithNameAndType (name, symbol_type); + } + 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::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(); + ObjectFile *objfile = GetObjectFile (); + if (objfile) + { + Symtab *symtab = objfile->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(); + ObjectFile *objfile = GetObjectFile (); + if (objfile) + { + Symtab *symtab = objfile->GetSymtab(); + if (symtab) + { + std::vector symbol_indexes; + symtab->FindAllSymbolsMatchingRexExAndType (regex, symbol_type, symbol_indexes); + SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); + } + } + return sc_list.GetSize() - initial_size; +} + +const TimeValue & +Module::GetModificationTime () const +{ + return m_mod_time; +} diff --git a/lldb/source/Core/ModuleChild.cpp b/lldb/source/Core/ModuleChild.cpp new file mode 100644 index 000000000000..f38fb4f6c36e --- /dev/null +++ b/lldb/source/Core/ModuleChild.cpp @@ -0,0 +1,52 @@ +//===-- ModuleChild.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/ModuleChild.h" + +using namespace lldb_private; + +ModuleChild::ModuleChild (Module* module) : + m_module(module) +{ +} + +ModuleChild::ModuleChild (const ModuleChild& rhs) : + m_module(rhs.m_module) +{ +} + +ModuleChild::~ModuleChild() +{ +} + +const ModuleChild& +ModuleChild::operator= (const ModuleChild& rhs) +{ + if (this != &rhs) + m_module = rhs.m_module; + return *this; +} + +Module * +ModuleChild::GetModule () +{ + return m_module; +} + +Module * +ModuleChild::GetModule () const +{ + return m_module; +} + +void +ModuleChild::SetModule (Module *module) +{ + m_module = module; +} diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp new file mode 100644 index 000000000000..ae6e27b1dd28 --- /dev/null +++ b/lldb/source/Core/ModuleList.cpp @@ -0,0 +1,626 @@ +//===-- ModuleList.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/ModuleList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/VariableList.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ModuleList constructor +//---------------------------------------------------------------------- +ModuleList::ModuleList() : + m_modules(), + m_modules_mutex (Mutex::eMutexTypeRecursive) +{ +} + +//---------------------------------------------------------------------- +// Copy constructor +//---------------------------------------------------------------------- +ModuleList::ModuleList(const ModuleList& rhs) : + m_modules(rhs.m_modules) +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const ModuleList& +ModuleList::operator= (const ModuleList& rhs) +{ + if (this != &rhs) + { + Mutex::Locker locker(m_modules_mutex); + m_modules = rhs.m_modules; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ModuleList::~ModuleList() +{ +} + +void +ModuleList::Append (ModuleSP &module_sp) +{ + Mutex::Locker locker(m_modules_mutex); + m_modules.push_back(module_sp); +} + +bool +ModuleList::AppendInNeeded (ModuleSP &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. + m_modules.push_back(module_sp); + return true; +} + +bool +ModuleList::Remove (ModuleSP &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); + return true; + } + } + return false; +} + + + +void +ModuleList::Clear() +{ + Mutex::Locker locker(m_modules_mutex); + m_modules.clear(); +} + +Module* +ModuleList::GetModulePointerAtIndex (uint32_t idx) const +{ + Mutex::Locker locker(m_modules_mutex); + if (idx < m_modules.size()) + return m_modules[idx].get(); + return NULL; +} + +ModuleSP +ModuleList::GetModuleAtIndex(uint32_t idx) +{ + Mutex::Locker locker(m_modules_mutex); + ModuleSP module_sp; + if (idx < m_modules.size()) + module_sp = m_modules[idx]; + return module_sp; +} + +size_t +ModuleList::FindFunctions (const ConstString &name, SymbolContextList &sc_list) +{ + 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)->FindFunctions (name, true, sc_list); + } + return sc_list.GetSize(); +} + +uint32_t +ModuleList::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variable_list) +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (name, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +uint32_t +ModuleList::FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variable_list) +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::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) +{ + Mutex::Locker locker(m_modules_mutex); + sc_list.Clear(); + collection::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(); +} + +class ModuleMatches +{ +public: + //-------------------------------------------------------------- + /// Construct with the user ID to look for. + //-------------------------------------------------------------- + ModuleMatches (const FileSpec *file_spec_ptr, + const ArchSpec *arch_ptr, + const UUID *uuid_ptr, + const ConstString *object_name) : + m_file_spec_ptr (file_spec_ptr), + m_arch_ptr (arch_ptr), + m_uuid_ptr (uuid_ptr), + m_object_name (object_name) + { + } + + //-------------------------------------------------------------- + /// Unary predicate function object callback. + //-------------------------------------------------------------- + bool + operator () (const ModuleSP& module_sp) const + { + if (m_file_spec_ptr) + { + if (!FileSpec::Equal (*m_file_spec_ptr, module_sp->GetFileSpec(), m_file_spec_ptr->GetDirectory())) + return false; + } + + if (m_arch_ptr) + { + if (module_sp->GetArchitecture() != *m_arch_ptr) + return false; + } + + if (m_uuid_ptr) + { + if (module_sp->GetUUID() != *m_uuid_ptr) + return false; + } + + if (m_object_name) + { + if (module_sp->GetObjectName() != *m_object_name) + return false; + } + return true; + } + +private: + //-------------------------------------------------------------- + // Member variables. + //-------------------------------------------------------------- + const FileSpec * m_file_spec_ptr; + const ArchSpec * m_arch_ptr; + const UUID * m_uuid_ptr; + const ConstString * m_object_name; +}; + +size_t +ModuleList::FindModules +( + const FileSpec *file_spec_ptr, + const ArchSpec *arch_ptr, + const UUID *uuid_ptr, + const ConstString *object_name, + ModuleList& matching_module_list +) const +{ + size_t existing_matches = matching_module_list.GetSize(); + ModuleMatches matcher (file_spec_ptr, arch_ptr, uuid_ptr, object_name); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator end = m_modules.end(); + collection::const_iterator pos; + + for (pos = std::find_if (m_modules.begin(), end, matcher); + pos != end; + pos = std::find_if (++pos, end, matcher)) + { + ModuleSP module_sp(*pos); + matching_module_list.Append(module_sp); + } + return matching_module_list.GetSize() - existing_matches; +} + +ModuleSP +ModuleList::FindModule (lldb_private::Module *module_ptr) +{ + 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::FindFirstModuleForFileSpec (const FileSpec &file_spec, const ConstString *object_name) +{ + ModuleSP module_sp; + ModuleMatches matcher (&file_spec, NULL, NULL, NULL); + + // Scope for "locker" + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator end = m_modules.end(); + collection::const_iterator pos = m_modules.begin(); + + pos = std::find_if (pos, end, matcher); + if (pos != end) + module_sp = (*pos); + } + 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); + } +} + + +bool +ModuleList::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) +{ + 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) +{ + // The address is already section offset so it has a module + uint32_t resolved_flags = 0; + Module *module = so_addr.GetModule(); + if (module) + { + resolved_flags = module->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) +{ + FileSpec file_spec(file_path); + return ResolveSymbolContextsForFileSpec (file_spec, line, check_inlines, resolve_scope, sc_list); +} + +uint32_t +ModuleList::ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + 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(); +} + +uint32_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 () +{ + static ModuleList g_shared_module_list; + return g_shared_module_list; +} + +const lldb::ModuleSP +ModuleList::GetModuleSP (lldb_private::Module *module_ptr) +{ + lldb::ModuleSP module_sp; + if (module_ptr) + { + ModuleList &shared_module_list = GetSharedModuleList (); + module_sp = shared_module_list.FindModule (module_ptr); + assert (module_sp.get()); // This might fire off a few times and we need to make sure it never fires... + } + return module_sp; +} + + +Error +ModuleList::GetSharedModule +( + const FileSpec& in_file_spec, + const ArchSpec& arch, + const UUID *uuid_ptr, + const ConstString *object_name_ptr, + off_t object_offset, + ModuleSP &module_sp, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr +) +{ + ModuleList &shared_module_list = GetSharedModuleList (); + char path[PATH_MAX]; + char uuid_cstr[64]; + + Error error; + + module_sp.reset(); + + if (did_create_ptr) + *did_create_ptr = false; + if (old_module_sp_ptr) + old_module_sp_ptr->reset(); + + + // First just try and get the file where it purports to be (path in + // in_file_spec), then check and uuid. + + if (in_file_spec) + { + // 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. + ModuleList matching_module_list; + Mutex::Locker locker(shared_module_list.m_modules_mutex); + if (shared_module_list.FindModules (&in_file_spec, &arch, uuid_ptr, object_name_ptr, 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 (uuid_ptr == NULL) + { + TimeValue file_spec_mod_time(in_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 (in_file_spec, arch, object_name_ptr, object_offset)); + if (module_sp) + { + // 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.Append(module_sp); + return error; + } + } + } + } + + // 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 (&in_file_spec, arch.IsValid() ? &arch : NULL, uuid_ptr); + + // Don't look for the file if it appears to be the same one we already + // checked for above... + if (file_spec != in_file_spec) + { + if (!file_spec.Exists()) + { + file_spec.GetPath(path, sizeof(path)); + if (file_spec.Exists()) + { + if (uuid_ptr && uuid_ptr->IsValid()) + uuid_ptr->GetAsCString(uuid_cstr, sizeof (uuid_cstr)); + else + uuid_cstr[0] = '\0'; + + + if (arch.IsValid()) + { + if (uuid_cstr[0]) + error.SetErrorStringWithFormat("'%s' does not contain the %s architecture and UUID %s.\n", path, arch.AsCString(), uuid_cstr[0]); + else + error.SetErrorStringWithFormat("'%s' does not contain the %s architecture.\n", path, arch.AsCString()); + } + } + else + { + error.SetErrorStringWithFormat("'%s' does not exist.\n", path); + } + 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. + Mutex::Locker locker(shared_module_list.m_modules_mutex); + ModuleList matching_module_list; + if (shared_module_list.FindModules (&file_spec, &arch, uuid_ptr, object_name_ptr, 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 (uuid_ptr == 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 (file_spec, arch, object_name_ptr, object_offset)); + if (module_sp) + { + if (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.Append(module_sp); + } + else + { + file_spec.GetPath(path, sizeof(path)); + + if (file_spec) + { + if (arch.IsValid()) + error.SetErrorStringWithFormat("Unable to open %s architecture in '%s'.\n", arch.AsCString(), path); + else + error.SetErrorStringWithFormat("Unable to open '%s'.\n", path); + } + else + { + if (uuid_ptr && uuid_ptr->IsValid()) + uuid_ptr->GetAsCString(uuid_cstr, sizeof (uuid_cstr)); + else + uuid_cstr[0] = '\0'; + + if (uuid_cstr[0]) + error.SetErrorStringWithFormat("Cannot locate a module for UUID '%s'.\n", uuid_cstr[0]); + else + error.SetErrorStringWithFormat("Cannot locate a module.\n", path, arch.AsCString()); + } + } + } + } + + return error; +} + diff --git a/lldb/source/Core/Options.cpp b/lldb/source/Core/Options.cpp new file mode 100644 index 000000000000..35a9fa40e1b6 --- /dev/null +++ b/lldb/source/Core/Options.cpp @@ -0,0 +1,700 @@ +//===-- 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/Core/Options.h" + +// C Includes +// C++ Includes +#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 () : + m_getopt_table () +{ +} + +Options::~Options () +{ +} + + +void +Options::ResetOptionValues () +{ + m_seen_options.clear(); +} + +void +Options::OptionSeen (int option_idx) +{ + m_seen_options.insert ((char) 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 = m_required_options.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 (m_required_options[i], m_seen_options)) + { + // Construct the set difference: remaining_options = {m_seen_options} - {m_required_options[i]} + OptionSet remaining_options; + OptionsSetDiff (m_seen_options, m_required_options[i], remaining_options); + // Check to see if remaining_options is a subset of m_optional_options[i] + if (IsASubset (remaining_options, m_optional_options[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; +} + +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 lldb::OptionDefinition *full_options_table = GetDefinitions(); + uint32_t usage_level = 0; + m_required_options.resize(1); + m_optional_options.resize(1); + + for (int i = 0; i < num_options; ++i) + { + // NOTE: Assumption: The full options table is ordered with usage level growing monotonically. + assert (full_options_table[i].usage_level >= usage_level); + + if (full_options_table[i].usage_level > usage_level) + { + // start a new level + usage_level = full_options_table[i].usage_level; + m_required_options.resize(m_required_options.size()+1); + m_optional_options.resize(m_optional_options.size()+1); + } + else + { + assert (m_required_options.empty() == false); + assert (m_optional_options.empty() == false); + } + + if (full_options_table[i].required) + m_required_options.back().insert(full_options_table[i].short_option); + else + m_optional_options.back().insert(full_options_table[i].short_option); + } +} + +uint32_t +Options::NumCommandOptions () +{ + const lldb::OptionDefinition *full_options_table = GetDefinitions (); + int i = 0; + + if (full_options_table != NULL) + { + while (full_options_table[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; + uint32_t j; + const lldb::OptionDefinition *full_options_table = GetDefinitions(); + + std::bitset<256> option_seen; + + m_getopt_table.resize(num_options + 1); + for (i = 0, j = 0; i < num_options; ++i) + { + char short_opt = full_options_table[i].short_option; + + if (option_seen.test(short_opt) == false) + { + m_getopt_table[j].name = full_options_table[i].long_option; + m_getopt_table[j].has_arg = full_options_table[i].option_has_arg; + m_getopt_table[j].flag = NULL; + m_getopt_table[j].val = full_options_table[i].short_option; + option_seen.set(short_opt); + ++j; + } + } + + //getopt_long requires a NULL final entry in the table: + + m_getopt_table[j].name = NULL; + m_getopt_table[j].has_arg = 0; + m_getopt_table[j].flag = NULL; + m_getopt_table[j].val = 0; + } + + return m_getopt_table.data(); +} + + +// 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(); + } +} + +void +Options::GenerateOptionUsage +( + Stream &strm, + CommandObject *cmd, + const char *program_name) +{ + uint32_t screen_width = 80; + const lldb::OptionDefinition *full_options_table = GetDefinitions(); + const uint32_t save_indent_level = strm.GetIndentLevel(); + const char *name; + + if (cmd) + name = cmd->GetCommandName(); + else + name = program_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. + + uint32_t usage_level = 0; + const uint32_t num_options = NumCommandOptions(); + uint32_t i; + for (i = 0; i < num_options; ++i) + { + if (i==0 || full_options_table[i].usage_level > usage_level) + { + // start a new level + usage_level = full_options_table[i].usage_level; + if (usage_level > 0) + { + strm.Printf ("\n"); + } + strm.Indent (name); + } + + // Add current option to the end of out_stream. + + if (full_options_table[i].required) + { + if (full_options_table[i].option_has_arg == required_argument) + { + strm.Printf (" -%c %s", + full_options_table[i].short_option, + full_options_table[i].argument_name); + } + else if (full_options_table[i].option_has_arg == optional_argument) + { + strm.Printf (" -%c [%s]", + full_options_table[i].short_option, + full_options_table[i].argument_name); + } + else + strm.Printf (" -%c", full_options_table[i].short_option); + } + else + { + if (full_options_table[i].option_has_arg == required_argument) + strm.Printf (" [-%c %s]", full_options_table[i].short_option, + full_options_table[i].argument_name); + else if (full_options_table[i].option_has_arg == optional_argument) + strm.Printf (" [-%c [%s]]", full_options_table[i].short_option, + full_options_table[i].argument_name); + else + strm.Printf (" [-%c]", full_options_table[i].short_option); + } + } + + 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. + + OptionSet options_seen; + OptionSet::iterator pos; + strm.IndentMore (5); + + int first_option_printed = 1; + for (i = 0; i < num_options; ++i) + { + // Only print out this option if we haven't already seen it. + pos = options_seen.find (full_options_table[i].short_option); + if (pos == options_seen.end()) + { + // Put a newline separation between arguments + if (first_option_printed) + first_option_printed = 0; + else + strm.EOL(); + + options_seen.insert (full_options_table[i].short_option); + strm.Indent (); + strm.Printf ("-%c ", full_options_table[i].short_option); + if (full_options_table[i].argument_name != NULL) + strm.PutCString(full_options_table[i].argument_name); + strm.EOL(); + strm.Indent (); + strm.Printf ("--%s ", full_options_table[i].long_option); + if (full_options_table[i].argument_name != NULL) + strm.PutCString(full_options_table[i].argument_name); + strm.EOL(); + + strm.IndentMore (5); + + if (full_options_table[i].usage_text) + OutputFormattedUsageText (strm, + full_options_table[i].usage_text, + screen_width); + if (full_options_table[i].enum_values != NULL) + { + strm.Indent (); + strm.Printf("Values: "); + for (int j = 0; full_options_table[i].enum_values[j].string_value != NULL; j++) + { + if (j == 0) + strm.Printf("%s", full_options_table[i].enum_values[j].string_value); + else + strm.Printf(" | %s", full_options_table[i].enum_values[j].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 = m_required_options.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 (m_required_options[i], m_optional_options[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, + lldb_private::CommandInterpreter *interpreter, + lldb_private::StringList &matches +) +{ + // 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 (int 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 != -1) + { + // We recognized it, if it an incomplete long option, complete it anyway (getopt_long 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 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 i = 0 ; opt_defs[i].short_option != 0 ; i++) + { + if (strstr(opt_defs[i].long_option, cur_opt_str + 2) == opt_defs[i].long_option) + { + std::string full_name ("--"); + full_name.append (opt_defs[i].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 (int j = 0; j < matches.GetSize(); j++) + { + if (matches.GetStringAtIndex(j) == 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, + interpreter, + 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, + lldb_private::CommandInterpreter *interpreter, + lldb_private::StringList &matches +) +{ + const OptionDefinition *opt_defs = GetDefinitions(); + std::auto_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].completionType; + if (completion_mask & CommandCompletions::eSourceFileCompletion + || completion_mask & CommandCompletions::eSymbolCompletion) + { + for (int 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); + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + // Search filters require a target... + if (target_sp != NULL) + filter_ap.reset (new SearchFilterByModule (target_sp, module_spec)); + } + break; + } + } + } + + return CommandCompletions::InvokeCommonCompletionCallbacks (completion_mask, + input.GetArgumentAtIndex (opt_arg_pos), + match_start_point, + max_return_elements, + interpreter, + filter_ap.get(), + matches); + +} diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp new file mode 100644 index 000000000000..3723e7cf89a2 --- /dev/null +++ b/lldb/source/Core/PluginManager.cpp @@ -0,0 +1,1133 @@ +//===-- 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/Core/PluginManager.h" + +#include +#include + +using namespace lldb_private; + +typedef enum PluginAction +{ + ePluginRegisterInstance, + ePluginUnregisterInstance, + ePluginGetInstanceAtIndex +}; + + +#pragma mark ABI + + +typedef struct ABIInstance +{ + ABIInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ABICreateInstance create_callback; +}; + +typedef std::vector ABIInstances; + +static bool +AccessABIInstances (PluginAction action, ABIInstance &instance, uint32_t index) +{ + static ABIInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ABIInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin + ( + const char *name, + const char *description, + ABICreateInstance create_callback + ) +{ + if (create_callback) + { + ABIInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessABIInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ABICreateInstance create_callback) +{ + if (create_callback) + { + ABIInstance instance; + instance.create_callback = create_callback; + return AccessABIInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackAtIndex (uint32_t idx) +{ + ABIInstance instance; + if (AccessABIInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ABIInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessABIInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + +#pragma mark Disassembler + + +typedef struct DisassemblerInstance +{ + DisassemblerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + DisassemblerCreateInstance create_callback; +}; + +typedef std::vector DisassemblerInstances; + +static bool +AccessDisassemblerInstances (PluginAction action, DisassemblerInstance &instance, uint32_t index) +{ + static DisassemblerInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + DisassemblerInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + DisassemblerCreateInstance create_callback +) +{ + if (create_callback) + { + DisassemblerInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessDisassemblerInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DisassemblerCreateInstance create_callback) +{ + if (create_callback) + { + DisassemblerInstance instance; + instance.create_callback = create_callback; + return AccessDisassemblerInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackAtIndex (uint32_t idx) +{ + DisassemblerInstance instance; + if (AccessDisassemblerInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + DisassemblerInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessDisassemblerInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + +#pragma mark DynamicLoader + + +typedef struct DynamicLoaderInstance +{ + DynamicLoaderInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + DynamicLoaderCreateInstance create_callback; +}; + +typedef std::vector DynamicLoaderInstances; + +static bool +AccessDynamicLoaderInstances (PluginAction action, DynamicLoaderInstance &instance, uint32_t index) +{ + static DynamicLoaderInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + DynamicLoaderInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + DynamicLoaderCreateInstance create_callback +) +{ + if (create_callback) + { + DynamicLoaderInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessDynamicLoaderInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DynamicLoaderCreateInstance create_callback) +{ + if (create_callback) + { + DynamicLoaderInstance instance; + instance.create_callback = create_callback; + return AccessDynamicLoaderInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx) +{ + DynamicLoaderInstance instance; + if (AccessDynamicLoaderInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + DynamicLoaderInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessDynamicLoaderInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + + +#pragma mark ObjectFile + +typedef struct ObjectFileInstance +{ + ObjectFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ObjectFileCreateInstance create_callback; +}; + +typedef std::vector ObjectFileInstances; + +static bool +AccessObjectFileInstances (PluginAction action, ObjectFileInstance &instance, uint32_t index) +{ + static ObjectFileInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ObjectFileInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + ObjectFileCreateInstance create_callback +) +{ + if (create_callback) + { + ObjectFileInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessObjectFileInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectFileCreateInstance create_callback) +{ + if (create_callback) + { + ObjectFileInstance instance; + instance.create_callback = create_callback; + return AccessObjectFileInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackAtIndex (uint32_t idx) +{ + ObjectFileInstance instance; + if (AccessObjectFileInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ObjectFileInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessObjectFileInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + +#pragma mark ObjectContainer + +typedef struct ObjectContainerInstance +{ + ObjectContainerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ObjectContainerCreateInstance create_callback; +}; + +typedef std::vector ObjectContainerInstances; + +static bool +AccessObjectContainerInstances (PluginAction action, ObjectContainerInstance &instance, uint32_t index) +{ + static ObjectContainerInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ObjectContainerInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + ObjectContainerCreateInstance create_callback +) +{ + if (create_callback) + { + ObjectContainerInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessObjectContainerInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectContainerCreateInstance create_callback) +{ + if (create_callback) + { + ObjectContainerInstance instance; + instance.create_callback = create_callback; + return AccessObjectContainerInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackAtIndex (uint32_t idx) +{ + ObjectContainerInstance instance; + if (AccessObjectContainerInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ObjectContainerInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessObjectContainerInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + +#pragma mark LogChannel + +typedef struct LogChannelInstance +{ + LogChannelInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + LogChannelCreateInstance create_callback; +}; + +typedef std::vector LogChannelInstances; + +static bool +AccessLogChannelInstances (PluginAction action, LogChannelInstance &instance, uint32_t index) +{ + static LogChannelInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + LogChannelInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + LogChannelCreateInstance create_callback +) +{ + if (create_callback) + { + LogChannelInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessLogChannelInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LogChannelCreateInstance create_callback) +{ + if (create_callback) + { + LogChannelInstance instance; + instance.create_callback = create_callback; + return AccessLogChannelInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +const char * +PluginManager::GetLogChannelCreateNameAtIndex (uint32_t idx) +{ + LogChannelInstance instance; + if (AccessLogChannelInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.name.c_str(); + return NULL; +} + + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackAtIndex (uint32_t idx) +{ + LogChannelInstance instance; + if (AccessLogChannelInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + LogChannelInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessLogChannelInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + +#pragma mark Process + +typedef struct ProcessInstance +{ + ProcessInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ProcessCreateInstance create_callback; +}; + +typedef std::vector ProcessInstances; + +static bool +AccessProcessInstances (PluginAction action, ProcessInstance &instance, uint32_t index) +{ + static ProcessInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ProcessInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + ProcessCreateInstance create_callback +) +{ + if (create_callback) + { + ProcessInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessProcessInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ProcessCreateInstance create_callback) +{ + if (create_callback) + { + ProcessInstance instance; + instance.create_callback = create_callback; + return AccessProcessInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackAtIndex (uint32_t idx) +{ + ProcessInstance instance; + if (AccessProcessInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ProcessInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessProcessInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + +#pragma mark SymbolFile + +typedef struct SymbolFileInstance +{ + SymbolFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + SymbolFileCreateInstance create_callback; +}; + +typedef std::vector SymbolFileInstances; + +static bool +AccessSymbolFileInstances (PluginAction action, SymbolFileInstance &instance, uint32_t index) +{ + static SymbolFileInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + SymbolFileInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + SymbolFileCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolFileInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessSymbolFileInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolFileCreateInstance create_callback) +{ + if (create_callback) + { + SymbolFileInstance instance; + instance.create_callback = create_callback; + return AccessSymbolFileInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackAtIndex (uint32_t idx) +{ + SymbolFileInstance instance; + if (AccessSymbolFileInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + SymbolFileInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessSymbolFileInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + +#pragma mark SymbolVendor + +typedef struct SymbolVendorInstance +{ + SymbolVendorInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + SymbolVendorCreateInstance create_callback; +}; + +typedef std::vector SymbolVendorInstances; + +static bool +AccessSymbolVendorInstances (PluginAction action, SymbolVendorInstance &instance, uint32_t index) +{ + static SymbolVendorInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + SymbolVendorInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + SymbolVendorCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolVendorInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessSymbolVendorInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolVendorCreateInstance create_callback) +{ + if (create_callback) + { + SymbolVendorInstance instance; + instance.create_callback = create_callback; + return AccessSymbolVendorInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackAtIndex (uint32_t idx) +{ + SymbolVendorInstance instance; + if (AccessSymbolVendorInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + SymbolVendorInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessSymbolVendorInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + diff --git a/lldb/source/Core/RegularExpression.cpp b/lldb/source/Core/RegularExpression.cpp new file mode 100644 index 000000000000..aca1671c181b --- /dev/null +++ b/lldb/source/Core/RegularExpression.cpp @@ -0,0 +1,154 @@ +//===-- 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" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +RegularExpression::RegularExpression() : + m_re(), + m_comp_err (1), + m_preg(), + m_matches() +{ + 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() +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re, flags); +} + +//---------------------------------------------------------------------- +// 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, int flags) +{ + Free(); + 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, size_t num_matches, int execute_flags) const +{ + int match_result = 1; + if (m_comp_err == 0) + { + if (num_matches > 0) + m_matches.resize(num_matches + 1); + else + m_matches.clear(); + + match_result = ::regexec (&m_preg, + s, + m_matches.size(), + const_cast(m_matches.data()), + execute_flags); + } + return match_result == 0; +} + +bool +RegularExpression::GetMatchAtIndex (const char* s, uint32_t idx, std::string& match_str) const +{ + if (idx <= m_preg.re_nsub && idx < m_matches.size()) + { + match_str.assign (s + m_matches[idx].rm_so, + m_matches[idx].rm_eo - m_matches[idx].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 +{ + 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; + } +} diff --git a/lldb/source/Core/Scalar.cpp b/lldb/source/Core/Scalar.cpp new file mode 100644 index 000000000000..56590b8d69e6 --- /dev/null +++ b/lldb/source/Core/Scalar.cpp @@ -0,0 +1,2084 @@ +//===-- 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 "lldb/Core/Args.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/DataExtractor.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 (eByteOrderHost == 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, eByteOrderHost); + } + else if (eByteOrderHost == 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, eByteOrderHost); + } + } + else + { + // We want all of the data + data.SetData((uint8_t*)&m_data, byte_size, eByteOrderHost); + } + return true; + } + data.Clear(); + return false; +} + +size_t +Scalar::GetByteSize() const +{ + switch (m_type) + { + default: + 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) + { + default: + 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: + default: + 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) + { + default: + break; + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: break; + 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(int)) + return e_sint; + if (byte_size <= sizeof(long)) + return e_slong; + if (byte_size <= sizeof(long long)) + return e_slonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForUnsignedIntegerWithByteSize (size_t byte_size) +{ + if (byte_size <= sizeof(unsigned int)) + return e_uint; + if (byte_size <= sizeof(unsigned long)) + return e_ulong; + if (byte_size <= sizeof(unsigned long long)) + return e_ulonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForFloatWithByteSize (size_t byte_size) +{ + if (byte_size == sizeof(float)) + return e_float; + if (byte_size == sizeof(double)) + return e_double; + if (byte_size == sizeof(long double)) + 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) + { + default: + 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) + { + default: + 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.slong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.ulong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.slonglong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.ulonglong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.flt; success = true; break; + case e_uint: m_data.uint = m_data.flt; success = true; break; + case e_slong: m_data.slong = m_data.flt; success = true; break; + case e_ulong: m_data.ulong = m_data.flt; success = true; break; + case e_slonglong: m_data.slonglong = m_data.flt; success = true; break; + case e_ulonglong: m_data.ulonglong = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.dbl; success = true; break; + case e_uint: m_data.uint = m_data.dbl; success = true; break; + case e_slong: m_data.slong = m_data.dbl; success = true; break; + case e_ulong: m_data.ulong = m_data.dbl; success = true; break; + case e_slonglong: m_data.slonglong = m_data.dbl; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.dbl; success = true; break; + case e_float: m_data.flt = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.ldbl; success = true; break; + case e_uint: m_data.uint = m_data.ldbl; success = true; break; + case e_slong: m_data.slong = m_data.ldbl; success = true; break; + case e_ulong: m_data.ulong = m_data.ldbl; success = true; break; + case e_slonglong: m_data.slonglong = m_data.ldbl; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ldbl; success = true; break; + case e_float: m_data.flt = m_data.ldbl; success = true; break; + case e_double: m_data.dbl = m_data.ldbl; success = true; break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +int +Scalar::SInt(int fail_value) const +{ + switch (m_type) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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(int)) + return m_data.uint; + else if (sizeof(m_data.flt) == sizeof(unsigned long)) + return m_data.ulong; + else if (sizeof(m_data.flt) == sizeof(unsigned long long)) + return m_data.ulonglong; + break; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(int)) + return m_data.uint; + else if (sizeof(m_data.dbl) == sizeof(unsigned long)) + return m_data.ulong; + else if (sizeof(m_data.dbl) == sizeof(unsigned long long)) + return m_data.ulonglong; + break; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(int)) + return m_data.uint; + else if (sizeof(m_data.ldbl) == sizeof(unsigned long)) + return m_data.ulong; + else if (sizeof(m_data.ldbl) == sizeof(unsigned long long)) + return m_data.ulonglong; + break; + } + return fail_value; +} + + + +long long +Scalar::SLongLong(long long fail_value) const +{ + switch (m_type) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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; + + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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; + + default: + 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; + + default: + 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; + + default: + 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; + + default: + 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; +} + +// 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, uint32_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) + { + default: + 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.\n", value_str); + else if (!UIntValueIsValidForSize (uval64, byte_size)) + error.SetErrorStringWithFormat ("Value 0x%llx is too large to fit in a %u byte unsigned integer value.\n", uval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_uint: m_data.uint = uval64; break; + case e_ulong: m_data.ulong = uval64; break; + case e_ulonglong: m_data.ulonglong = uval64; break; + default: + error.SetErrorStringWithFormat ("Unsupported unsigned integer byte size: %u.\n", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("Unsupported unsigned integer byte size: %u.\n", 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.\n", value_str); + else if (!SIntValueIsValidForSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("Value 0x%llx is too large to fit in a %u byte signed integer value.\n", sval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_sint: m_data.sint = sval64; break; + case e_slong: m_data.slong = sval64; break; + case e_slonglong: m_data.slonglong = sval64; break; + default: + error.SetErrorStringWithFormat ("Unsupported signed integer byte size: %u.\n", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("Unsupported signed integer byte size: %u.\n", 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.\n", 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.\n", 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.\n", value_str); + } + else + { + error.SetErrorStringWithFormat ("Unsupported float byte size: %u.\n", byte_size); + return error; + } + break; + + case eEncodingVector: + error.SetErrorString ("Vector encoding unsupported."); + break; + } + if (error.Fail()) + m_type = e_void; + + return error; +} + +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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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/lldb/source/Core/SearchFilter.cpp b/lldb/source/Core/SearchFilter.cpp new file mode 100644 index 000000000000..4c54a9128014 --- /dev/null +++ b/lldb/source/Core/SearchFilter.cpp @@ -0,0 +1,435 @@ +//===-- 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/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(lldb::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::SymbolContextPasses +( + const SymbolContext &context, + lldb::SymbolContextItem scope +) +{ + return true; +} + +bool +SearchFilter::AddressPasses (Address &address) +{ + return true; +} + +bool +SearchFilter::CompUnitPasses (FileSpec &fileSpec) +{ + return true; +} + +bool +SearchFilter::CompUnitPasses (CompileUnit &compUnit) +{ + return true; +} + +void +SearchFilter::GetDescription (Stream *s) +{ + s->PutCString("No Filter"); +} + +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 == NULL) + 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 == NULL) + return; + empty_sc.target_sp = m_target_sp; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + else + { + size_t numModules = modules.GetSize(); + + for (int i = 0; i < numModules; i++) + { + ModuleSP module_sp(modules.GetModuleAtIndex(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) +{ + Searcher::CallbackReturn shouldContinue; + + if (searcher.GetDepth () >= Searcher::eDepthModule) + { + if (!context.module_sp) + { + size_t n_modules = m_target_sp->GetImages().GetSize(); + for (int i = 0; i < n_modules; i++) + { + // If this is the last level supplied, then call the callback directly, + // otherwise descend. + ModuleSP module_sp(m_target_sp->GetImages().GetModuleAtIndex(i)); + if (!ModulePasses (module_sp)) + continue; + + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(m_target_sp, module_sp); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + if (shouldContinue == Searcher::eCallbackReturnStop + || shouldContinue == Searcher::eCallbackReturnPop) + return shouldContinue; + } + else + { + shouldContinue = DoCUIteration(module_sp, context, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return shouldContinue; + else if (shouldContinue == Searcher::eCallbackReturnPop) + continue; + } + } + } + else + { + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(context.module_sp.get()); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + } + else + { + return DoCUIteration(context.module_sp, context, searcher); + } + } + + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::CallbackReturn +SearchFilter::DoCUIteration (const ModuleSP &module_sp, const SymbolContext &context, Searcher &searcher) +{ + Searcher::CallbackReturn shouldContinue; + if (context.comp_unit == NULL) + { + uint32_t num_comp_units = module_sp->GetNumCompileUnits(); + for (uint32_t i = 0; i < num_comp_units; i++) + { + CompUnitSP cu_sp (module_sp->GetCompileUnitAtIndex (i)); + 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; +} + +//---------------------------------------------------------------------- +// SearchFilterByModule: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModule constructors +//---------------------------------------------------------------------- + +SearchFilterByModule::SearchFilterByModule (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::Compare (module_sp->GetFileSpec(), m_module_spec, false) == 0) + return true; + else + return false; +} + +bool +SearchFilterByModule::ModulePasses (const FileSpec &spec) +{ + if (FileSpec::Compare(spec, m_module_spec, false) == 0) + return true; + else + return false; +} + +bool +SearchFilterByModule::SymbolContextPasses +( + const SymbolContext &context, + lldb::SymbolContextItem scope + ) +{ + if (!(scope & eSymbolContextModule)) + return false; + + if (context.module_sp && FileSpec::Compare (context.module_sp->GetFileSpec(), m_module_spec, false) == 0) + return true; + else + return false; +} + +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. + + ModuleList matching_modules; + // const size_t num_matching_modules = m_target_sp->GetImages().FindModules(&m_module_spec, NULL, NULL, NULL, matching_modules); + for (int i = 0; i < m_target_sp->GetImages().GetSize (); i++) + { + Module* module = m_target_sp->GetImages().GetModulePointerAtIndex(i); + if (FileSpec::Compare (m_module_spec, module->GetFileSpec(), false) == 0) + { + SymbolContext matchingContext(m_target_sp, module->GetSP()); + Searcher::CallbackReturn shouldContinue; + + shouldContinue = DoModuleIteration(matchingContext, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return; + } + } +} + +void +SearchFilterByModule::GetDescription (Stream *s) +{ + s->PutCString("In module "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec.GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec.GetFilename().AsCString("")); + } +} + +void +SearchFilterByModule::Dump (Stream *s) const +{ + +} diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp new file mode 100644 index 000000000000..57c519918f23 --- /dev/null +++ b/lldb/source/Core/Section.cpp @@ -0,0 +1,791 @@ +//===-- 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/Process.h" + +using namespace lldb; +using namespace lldb_private; + +Section::Section +( + Section *parent, + Module* module, + user_id_t sect_id, + const ConstString &name, + SectionType sect_type, + addr_t file_addr, + addr_t byte_size, + uint64_t file_offset, + uint64_t file_size, + uint32_t flags +) : + ModuleChild (module), + UserID (sect_id), + Flags (flags), + m_parent (parent), + m_name (name), + m_type (sect_type), + 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_linked_section(NULL), + m_linked_offset (0) +{ +} + +//Section::Section +//( +// Section *parent, +// Module* module, +// user_id_t sect_id, +// const ConstString &name, +// const AddressRange *file_vm_range, +// uint64_t file_offset, +// uint64_t file_size, +// uint32_t flags +//) : +// ModuleChild (module), +// UserID (sect_id), +// Flags (flags), +// m_parent (parent), +// m_name (name), +// m_range (), +// m_file_offset (file_offset), +// m_file_size (file_size), +// m_children (), +// m_fake (false) +//{ +// if (file_vm_range) +// m_range = *file_vm_range; +//} + +Section::~Section() +{ +} + + +// Get a valid shared pointer to this section object +SectionSP +Section::GetSharedPointer() const +{ + SectionSP this_sp; + if (m_parent) + this_sp = m_parent->GetChildren().GetSharedPointer (this, false); + else + { + ObjectFile *objfile = m_module->GetObjectFile(); + if (objfile) + { + SectionList *section_list = objfile->GetSectionList(); + if (section_list) + this_sp = section_list->GetSharedPointer (this, false); + } + } + return this_sp; +} + + + +ConstString& +Section::GetName() +{ + if (m_linked_section) + return const_cast
(m_linked_section)->GetName(); + return m_name; +} + +const ConstString& +Section::GetName() const +{ + if (m_linked_section) + return m_linked_section->GetName(); + return m_name; +} + +SectionList& +Section::GetChildren() +{ + return m_children; +} + +const SectionList& +Section::GetChildren() const +{ + return m_children; +} + +addr_t +Section::GetFileAddress () const +{ + if (m_parent) + { + // 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 m_parent->GetFileAddress() + m_file_addr; + } + // This section has no parent, so m_file_addr is the file base address + return m_file_addr; +} + +addr_t +Section::GetLinkedFileAddress () const +{ + if (m_linked_section) + return m_linked_section->GetFileAddress() + m_linked_offset; + return LLDB_INVALID_ADDRESS; +} + +addr_t +Section::GetOffset () const +{ + if (m_parent) + { + // This section has a parent which means m_file_addr is an offset. + return m_file_addr; + } + + // This section has no parent, so there is no offset to be had + return 0; +} + +addr_t +Section::GetByteSize () const +{ + return m_byte_size; +} + +void +Section::SetByteSize (addr_t byte_size) +{ + m_byte_size = byte_size; +} + + +addr_t +Section::GetLoadBaseAddress (Process *process) const +{ + addr_t load_base_addr = LLDB_INVALID_ADDRESS; + if (m_linked_section) + { + load_base_addr = m_linked_section->GetLoadBaseAddress(process) + m_linked_offset; + } + else + if (m_parent) + { + load_base_addr = m_parent->GetLoadBaseAddress (process); + if (load_base_addr != LLDB_INVALID_ADDRESS) + load_base_addr += GetOffset(); + } + else + { + load_base_addr = process->GetSectionLoadAddress(this); + } + + return load_base_addr; +} + +bool +Section::ResolveContainedAddress (addr_t offset, Address &so_addr) const +{ + const uint32_t num_children = m_children.GetSize(); + if (num_children > 0) + { + for (uint32_t i=0; iGetOffset(); + if (child_offset <= offset && offset - child_offset < child_section->GetByteSize()) + return child_section->ResolveContainedAddress (offset - child_offset, so_addr); + } + } + if (m_linked_section) + { + so_addr.SetOffset(m_linked_offset + offset); + so_addr.SetSection(m_linked_section); + } + else + { + so_addr.SetOffset(offset); + so_addr.SetSection(this); + } + return true; +} + +uint64_t +Section::GetFileOffset() const +{ + return m_file_offset; +} + +uint64_t +Section::GetFileSize() const +{ + return m_file_size; +} + +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; +} + +bool +Section::ContainsLinkedFileAddress (addr_t vm_addr) const +{ + const addr_t linked_file_addr = GetLinkedFileAddress(); + if (linked_file_addr != LLDB_INVALID_ADDRESS) + { + if (linked_file_addr <= vm_addr) + { + const addr_t offset = vm_addr - linked_file_addr; + return offset < GetByteSize(); + } + } + return false; +} + +int +Section::Compare (const Section& a, const Section& b) +{ + if (&a == &b) + return 0; + + const Module* a_module = a.GetModule(); + const Module* b_module = b.GetModule(); + if (a_module == b_module) + { + 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 < b_module) + return -1; + else + return 1; // We already know the modules aren't equal + } +} + + +void +Section::Dump(Stream *s, Process *process) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->Printf("0x%8.8x ", GetID()); + bool resolved = true; + addr_t addr = LLDB_INVALID_ADDRESS; + + if (GetByteSize() == 0) + s->Printf("%39s", ""); + else + { + if (process) + addr = GetLoadBaseAddress (process); + + if (addr == LLDB_INVALID_ADDRESS) + { + if (process) + resolved = false; + addr = GetFileAddress(); + } + + VMRange range(addr, addr + m_byte_size); + range.Dump (s, 0); + } + + s->Printf("%c 0x%8.8llx 0x%8.8llx 0x%8.8x ", resolved ? ' ' : '*', m_file_offset, m_file_size, GetAllFlagBits()); + + DumpName (s); + + s->EOL(); + + if (m_linked_section) + { + addr = LLDB_INVALID_ADDRESS; + + if (process) + { + addr = m_linked_section->GetLoadBaseAddress(process); + if (addr != LLDB_INVALID_ADDRESS) + addr += m_linked_offset; + } + + if (addr == LLDB_INVALID_ADDRESS) + { + if (process) + resolved = false; + addr = m_linked_section->GetFileAddress() + m_linked_offset; + } + + int indent = (sizeof(void*) + 1 + sizeof(user_id_t) + 1) * 2 + 3 + s->GetIndentLevel(); + s->Printf("%*.*s", indent, indent, ""); + VMRange linked_range(addr, addr + m_byte_size); + linked_range.Dump (s, 0); + indent = 3 * (sizeof(uint32_t) * 2 + 2 + 1) + 1; + s->Printf("%c%*.*s", resolved ? ' ' : '*', indent, indent, ""); + + m_linked_section->DumpName(s); + s->Printf(" + 0x%llx\n", m_linked_offset); + } + + m_children.Dump(s, process, false); +} + +void +Section::DumpName (Stream *s) const +{ + if (m_linked_section) + return m_linked_section->DumpName(s); + else if (m_parent == NULL) + { + // The top most section prints the module basename + const char *module_basename = m_module->GetFileSpec().GetFilename().AsCString(); + if (module_basename && module_basename[0]) + s->Printf("%s.", module_basename); + } + else + { + m_parent->DumpName (s); + s->PutChar('.'); + } + m_name.Dump(s); +} + +//---------------------------------------------------------------------- +// Get the section data from a complete contiguous copy of the +// entire executable image. +//---------------------------------------------------------------------- +size_t +Section::GetSectionDataFromImage (const DataExtractor& image_data, DataExtractor& section_data) const +{ + size_t file_size = GetByteSize(); + if (file_size > 0) + { + off_t file_offset = GetFileOffset(); + if (section_data.SetData (image_data, file_offset, file_size) == file_size) + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// Get the section data the file on disk +//---------------------------------------------------------------------- +size_t +Section::ReadSectionDataFromObjectFile(const ObjectFile* objfile, DataExtractor& section_data) const +{ + if (objfile == NULL) + return 0; + + const FileSpec& file = objfile->GetFileSpec(); + + if (file) + { + size_t section_file_size = GetByteSize(); + if (section_file_size > 0) + { + off_t section_file_offset = GetFileOffset() + objfile->GetOffset(); + DataBufferSP sectionDataSP(file.ReadFileContents(section_file_offset, section_file_size)); + + section_data.SetByteOrder(objfile->GetByteOrder()); + section_data.SetAddressByteSize(objfile->GetAddressByteSize()); + return section_data.SetData (sectionDataSP); + } + } + return 0; +} + +size_t +Section::MemoryMapSectionDataFromObjectFile(const ObjectFile* objfile, DataExtractor& section_data) const +{ + if (objfile == NULL) + return 0; + + const FileSpec& file = objfile->GetFileSpec(); + + if (file) + { + size_t section_file_size = GetFileSize(); + if (section_file_size > 0) + { + off_t section_file_offset = GetFileOffset() + objfile->GetOffset(); + DataBufferSP sectionDataSP(file.MemoryMapFileContents(section_file_offset, section_file_size)); + section_data.SetByteOrder(objfile->GetByteOrder()); + section_data.SetAddressByteSize(objfile->GetAddressByteSize()); + return section_data.SetData (sectionDataSP); + } + } + return 0; +} + +bool +Section::IsFake() const +{ + return m_fake; +} + +void +Section::SetIsFake(bool fake) +{ + m_fake = fake; +} + + +bool +Section::IsDescendant (const Section *section) +{ + if (this == section) + return true; + if (m_parent) + return m_parent->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; +} + +void +Section::SetLinkedLocation (const Section *linked_section, uint64_t linked_offset) +{ + if (linked_section) + m_module = linked_section->GetModule(); + m_linked_section = linked_section; + m_linked_offset = linked_offset; +} + +const Section * +Section::GetLinkedSection () const +{ + return m_linked_section; +} + +uint64_t +Section::GetLinkedOffset () const +{ + return m_linked_offset; +} + +#pragma mark SectionList + +SectionList::SectionList () : + m_sections() +{ +} + + +SectionList::~SectionList () +{ +} + +uint32_t +SectionList::AddSection (SectionSP& sect_sp) +{ + uint32_t section_index = m_sections.size(); + m_sections.push_back(sect_sp); + return section_index; +} + +uint32_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; +} + +uint32_t +SectionList::AddUniqueSection (SectionSP& sect_sp) +{ + uint32_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, 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::GetSize () const +{ + return m_sections.size(); +} + +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 (uint32_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) + { + 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)->GetName() == section_dstr) + { + sect_sp = *sect_iter; + } + else + { + sect_sp = (*sect_iter)->GetChildren().FindSectionByName(section_dstr); + } + } + } + return sect_sp; +} +// +//SectionSP +//SectionList::FindSectionByNames (const char *s, ...) const +//{ +// SectionSP sect_sp; +// va_list ap; +// va_start(ap, s); +// uint32_t idx = 0; +// for (const char *sect_name = s; sect_name != NULL; sect_name = va_arg(ap, const char *)) +// { +// printf("[%u] %s\n", idx++, sect_name); +// } +// va_end(ap); +// 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::GetSharedPointer (const Section *section, bool check_children) const +{ + SectionSP sect_sp; + if (section) + { + 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->get() == section) + { + sect_sp = *sect_iter; + break; + } + else if (check_children) + { + sect_sp = (*sect_iter)->GetChildren().GetSharedPointer (section, true); + } + } + } + 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; +} + + +SectionSP +SectionList::FindSectionContainingLinkedFileAddress (addr_t vm_addr) 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->ContainsLinkedFileAddress (vm_addr)) + { + sect_sp = *sect_iter; + break; + } + } + return sect_sp; +} + +bool +SectionList::ContainsSection(user_id_t sect_id) const +{ + return FindSectionByID (sect_id).get() != NULL; +} + +void +SectionList::Dump (Stream *s, Process *process, bool show_header) const +{ + if (show_header && !m_sections.empty()) + { + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->PutCString( "SectionList\n"); + s->IndentMore(); + s->Printf("%*s", 2*(sizeof(void *) + 2), ""); + s->Indent(); + s->Printf("SectID %s Address File Off. File Size Flags Section Name\n", process ? "Load" : "File"); + s->Printf("%*s", 2*(sizeof(void *) + 2), ""); + 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, process); + } + + 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/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp new file mode 100644 index 000000000000..2786fe407204 --- /dev/null +++ b/lldb/source/Core/SourceManager.cpp @@ -0,0 +1,305 @@ +//===-- 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/Core/SourceManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +static inline bool is_newline_char(char ch) +{ + return ch == '\n' || ch == '\r'; +} + + +//---------------------------------------------------------------------- +// SourceManager constructor +//---------------------------------------------------------------------- +SourceManager::SourceManager() : + m_file_cache (), + m_last_file_sp (), + m_last_file_line (0), + m_last_file_context_before (0), + m_last_file_context_after (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SourceManager::~SourceManager() +{ +} + +size_t +SourceManager::DisplaySourceLines +( + const FileSpec &file_spec, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + Stream *s +) +{ + m_last_file_sp = GetFile (file_spec); + m_last_file_line = line + context_after + 1; + m_last_file_context_before = context_before; + m_last_file_context_after = context_after; + if (m_last_file_sp.get()) + return m_last_file_sp->DisplaySourceLines (line, context_before, context_after, s); + + return 0; +} + +SourceManager::FileSP +SourceManager::GetFile (const FileSpec &file_spec) +{ + FileSP file_sp; + FileCache::iterator pos = m_file_cache.find(file_spec); + if (pos != m_file_cache.end()) + file_sp = pos->second; + else + { + file_sp.reset (new File (file_spec)); + m_file_cache[file_spec] = file_sp; + } + return file_sp; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile +( + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s +) +{ + if (line == 0) + { + if (m_last_file_line != 0 + && m_last_file_line != UINT32_MAX) + line = m_last_file_line + context_before; + else + line = 1; + } + + m_last_file_line = line + context_after + 1; + m_last_file_context_before = context_before; + m_last_file_context_after = context_after; + + if (context_before == UINT32_MAX) + context_before = 0; + if (context_after == UINT32_MAX) + context_after = 10; + + if (m_last_file_sp.get()) + { + const uint32_t start_line = line <= context_before ? 1 : line - context_before; + const uint32_t end_line = line + context_after; + uint32_t curr_line; + for (curr_line = start_line; curr_line <= end_line; ++curr_line) + { + if (!m_last_file_sp->LineIsValid (curr_line)) + { + m_last_file_line = UINT32_MAX; + break; + } + + s->Printf("%4u %2.2s\t", curr_line, curr_line == line ? current_line_cstr : ""); + if (m_last_file_sp->DisplaySourceLines (curr_line, 0, 0, s) == 0) + { + m_last_file_line = UINT32_MAX; + break; + } + } + } + return 0; +} + +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 +) +{ + bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec); + + if (!same_as_previous) + m_last_file_sp = GetFile (file_spec); + + if (line == 0) + { + if (!same_as_previous) + m_last_file_line = 0; + } + + return DisplaySourceLinesWithLineNumbersUsingLastFile (line, context_before, context_after, current_line_cstr, s); +} + +size_t +SourceManager::DisplayMoreWithLineNumbers (Stream *s) +{ + if (m_last_file_sp) + { + if (m_last_file_line == UINT32_MAX) + return 0; + DisplaySourceLinesWithLineNumbersUsingLastFile (0, m_last_file_context_before, m_last_file_context_after, "", s); + } + return 0; +} + + + +SourceManager::File::File(const FileSpec &file_spec) : + m_file_spec(file_spec), + m_data_sp(file_spec.ReadFileContents ()), + m_offsets() +{ +} + +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) +{ + 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; +} + +bool +SourceManager::File::FileSpecMatches (const FileSpec &file_spec) +{ + return FileSpec::Compare (m_file_spec, file_spec, false) == 0; +} + + +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)) + { + 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"); + } + + } + else + { + // Calculate all line offsets up to "line" + assert(!"Not implemented yet"); + } + return false; +} diff --git a/lldb/source/Core/State.cpp b/lldb/source/Core/State.cpp new file mode 100644 index 000000000000..cf9a8345818c --- /dev/null +++ b/lldb/source/Core/State.cpp @@ -0,0 +1,87 @@ +//===-- 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" + +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 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; +} + +bool +lldb_private::StateIsRunningState (StateType state) +{ + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + return true; + + case eStateDetached: + case eStateInvalid: + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + default: + break; + } + return false; +} + +bool +lldb_private::StateIsStoppedState (StateType state) +{ + switch (state) + { + case eStateInvalid: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + default: + break; + + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + return true; + } + return false; +} diff --git a/lldb/source/Core/Stream.cpp b/lldb/source/Core/Stream.cpp new file mode 100644 index 000000000000..a0de2d431e9f --- /dev/null +++ b/lldb/source/Core/Stream.cpp @@ -0,0 +1,776 @@ +//===-- 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" + +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 (eByteOrderHost), + 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". +//------------------------------------------------------------------ +int +Stream::PutSLEB128 (int64_t sval) +{ + int bytes_written = 0; + if (m_flags.IsSet(eBinary)) + { + bool more = true; + bool negative = (sval < 0); + while (more) + { + uint8_t byte = sval & 0x7fu; + sval >>= 7; + assert((!negative && sval >= 0) || (negative && sval < 0)); + /* 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%lli", sval); + } + + return bytes_written; + +} + +//------------------------------------------------------------------ +// Put an ULEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +int +Stream::PutULEB128 (uint64_t uval) +{ + int bytes_written = 0; + if (m_flags.IsSet(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%llx", uval); + } + return bytes_written; +} + +//------------------------------------------------------------------ +// Print a raw NULL terminated C string to the stream using the +// printf format in "format". +//------------------------------------------------------------------ +int +Stream::PutCString (const char *cstr) +{ + int cstr_len = strlen(cstr); + // when in binary mode, emit the NULL terminator + if (m_flags.IsSet(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, int 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*llx%s", prefix, addr_width, addr, suffix); + Printf ("%s0x%0*llx%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, int addr_size, const char *prefix, const char *suffix) +{ + if (prefix != NULL) + PutCString (prefix); + Address (lo_addr, addr_size, "["); + Address (hi_addr, addr_size, "-", ")"); +} + + +int +Stream::PutChar (char ch) +{ + return Write (&ch, 1); +} + + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +int +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. +//------------------------------------------------------------------ +int +Stream::PrintfVarArg (const char *format, va_list args) +{ + char str[1024]; + va_list args_copy; + + va_copy (args_copy, args); + + int bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + int length = vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + va_end (args); + // Include the NULL termination byte for binary output + if (m_flags.IsSet(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.IsSet(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 +//------------------------------------------------------------------ +int +Stream::EOL() +{ + return PutChar ('\n'); +} + +//------------------------------------------------------------------ +// Indent the current line using the current indentation level and +// print an optional string following the idenatation spaces. +//------------------------------------------------------------------ +int +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 ("%lli", 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 +//------------------------------------------------------------------ +uint8_t +Stream::GetAddressByteSize() const +{ + return m_addr_size; +} + +//------------------------------------------------------------------ +// Set the address size in bytes +//------------------------------------------------------------------ +void +Stream::SetAddressByteSize(uint8_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.IsSet(eVerbose); +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetDebug() const +{ + return m_flags.IsSet(eDebug); +} + +//------------------------------------------------------------------ +// The flags get accessor +//------------------------------------------------------------------ +Flags& +Stream::GetFlags() +{ + return m_flags; +} + +//------------------------------------------------------------------ +// The flags const get accessor +//------------------------------------------------------------------ +const Flags& +Stream::GetFlags() const +{ + return m_flags; +} + +int +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 + + int i; + char str[1024]; + int bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + int 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 (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; +} + +int +Stream::PutHex8 (uint8_t uvalue) +{ + return _PutHex8 (uvalue, m_flags.IsSet(eAddPrefix)); +} + +int +Stream::PutHex16 (uint16_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.IsSet(eAddPrefix); + int bytes_written = 0; + int byte; + if (byte_order == eByteOrderLittle) + { + for (byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + else + { + for (byte = sizeof(uvalue)-1; byte >= 0; --byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + return bytes_written; +} + +int +Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.IsSet(eAddPrefix); + int bytes_written = 0; + int byte; + if (byte_order == eByteOrderLittle) + { + for (byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + else + { + for (byte = sizeof(uvalue)-1; byte >= 0; --byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + return bytes_written; +} + +int +Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.IsSet(eAddPrefix); + int bytes_written = 0; + int byte; + if (byte_order == eByteOrderLittle) + { + for (byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + else + { + for (byte = sizeof(uvalue)-1; byte >= 0; --byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + return bytes_written; +} + +int +Stream::PutMaxHex64 +( + uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order +) +{ + switch (byte_size) + { + case 1: return PutHex8 (uvalue); + case 2: return PutHex16 (uvalue); + case 4: return PutHex32 (uvalue); + case 8: return PutHex64 (uvalue); + } + return 0; +} + +int +Stream::PutPointer (void *ptr) +{ + return PutRawBytes (&ptr, sizeof(ptr), eByteOrderHost, eByteOrderHost); +} + +int +Stream::PutFloat(float f, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&f, sizeof(f), eByteOrderHost, byte_order); +} + +int +Stream::PutDouble(double d, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&d, sizeof(d), eByteOrderHost, byte_order); +} + +int +Stream::PutLongDouble(long double ld, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&ld, sizeof(ld), eByteOrderHost, byte_order); +} + +int +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; + + int bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + int i; + bool binary_is_clear = m_flags.IsClear (eBinary); + m_flags.Set (eBinary); + if (src_byte_order == dst_byte_order) + { + for (i=0;i=0; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_clear) + m_flags.Clear (eBinary); + + return bytes_written; +} + +int +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; + + int bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + int i; + bool binary_is_set = m_flags.IsSet(eBinary); + m_flags.Clear(eBinary); + if (src_byte_order == dst_byte_order) + { + for (i=0;i=0; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return bytes_written; +} + +int +Stream::PutCStringAsRawHex8 (const char *s) +{ + int bytes_written = 0; + bool binary_is_set = m_flags.IsSet(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, eByteOrderHost); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderBig); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderHost); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderBig); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderHost); + 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/lldb/source/Core/StreamFile.cpp b/lldb/source/Core/StreamFile.cpp new file mode 100644 index 000000000000..9c6c508b49e7 --- /dev/null +++ b/lldb/source/Core/StreamFile.cpp @@ -0,0 +1,132 @@ +//===-- 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 +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StreamFile constructor +//---------------------------------------------------------------------- +StreamFile::StreamFile () : + Stream (), + m_file (NULL), + m_path_name (), + m_close_file (false) +{ +} + +StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order, FILE *f) : + Stream (flags, addr_size, byte_order), + m_file(f), + m_path_name (), + m_close_file(false) +{ +} + +StreamFile::StreamFile(FILE *f) : + Stream (), + m_file(f), + m_path_name (), + m_close_file(false) +{ +} + +StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order, const char *path, const char *permissions) : + Stream (flags, addr_size, byte_order), + m_file (NULL), + m_path_name (path), + m_close_file(false) +{ + Open(path, permissions); +} + +StreamFile::StreamFile(const char *path, const char *permissions) : + Stream (), + m_file (NULL), + m_path_name (path), + m_close_file(false) +{ + Open(path, permissions); +} + + +StreamFile::~StreamFile() +{ + Close (); +} + +void +StreamFile::Close () +{ + if (m_close_file && m_file != NULL) + ::fclose (m_file); + m_file = NULL; + m_close_file = false; +} + +bool +StreamFile::Open (const char *path, const char *permissions) +{ + Close(); + if (path && path[0]) + { + if ((m_path_name.size() == 0) + || (m_path_name.compare(path) != 0)) + m_path_name = path; + m_file = ::fopen (path, permissions); + if (m_file != NULL) + m_close_file = true; + } + return m_file != NULL; +} + +void +StreamFile::Flush () +{ + if (m_file) + ::fflush (m_file); +} + +int +StreamFile::Write (const void *s, size_t length) +{ + if (m_file) + return ::fwrite (s, 1, length, m_file); + return 0; +} + +FILE * +StreamFile::GetFileHandle() +{ + return m_file; +} + +void +StreamFile::SetFileHandle (FILE *file, bool close_file) +{ + Close(); + m_file = file; + m_close_file = close_file; +} + +const char * +StreamFile::GetFilePathname () +{ + if (m_path_name.size() == 0) + return NULL; + else + return m_path_name.c_str(); +} diff --git a/lldb/source/Core/StreamString.cpp b/lldb/source/Core/StreamString.cpp new file mode 100644 index 000000000000..ccd9f9725824 --- /dev/null +++ b/lldb/source/Core/StreamString.cpp @@ -0,0 +1,81 @@ +//===-- 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... +} + +int +StreamString::Write (const void *s, size_t length) +{ + m_packet.append ((char *)s, length); + return length; +} + +void +StreamString::Clear() +{ + m_packet.clear(); +} + +void +StreamString::Dump(FILE *f) +{ + int size = GetSize(); + if (size > 0) + fprintf(f, "%*.*s", size, size, m_packet.c_str()); +} + +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; +} + diff --git a/lldb/source/Core/StringList.cpp b/lldb/source/Core/StringList.cpp new file mode 100644 index 000000000000..cb96fb0f7b91 --- /dev/null +++ b/lldb/source/Core/StringList.cpp @@ -0,0 +1,200 @@ +//===-- 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 + +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 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) +{ + uint32_t len = strings.GetSize(); + + for (int i = 0; i < len; ++i) + m_strings.push_back (strings.GetStringAtIndex(i)); +} + +uint32_t +StringList::GetSize () +{ + return m_strings.size(); +} + +const char * +StringList::GetStringAtIndex (size_t idx) +{ + if (idx < m_strings.size()) + return m_strings[idx].c_str(); + return NULL; +} + +void +StringList::Clear () +{ + m_strings.clear(); +} + +void +StringList::LongestCommonPrefix (std::string &common_prefix) +{ + //arg_sstr_collection::iterator pos, end = m_args.end(); + int pos = 0; + int end = m_strings.size(); + + if (pos == end) + common_prefix.clear(); + else + common_prefix = m_strings[pos]; + + for (++pos; pos != end; ++pos) + { + int 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 (int 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; + + int idx = 0; + while (idx < m_strings.size()) + { + if (m_strings[idx].empty()) + DeleteStringAtIndex(idx); + else + idx++; + } +} diff --git a/lldb/source/Core/TTYState.cpp b/lldb/source/Core/TTYState.cpp new file mode 100644 index 000000000000..6ba415832d4e --- /dev/null +++ b/lldb/source/Core/TTYState.cpp @@ -0,0 +1,203 @@ +//===-- TTYState.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/TTYState.h" +#include +#include +#include + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +TTYState::TTYState() : + m_fd(-1), + m_tflags(-1), + m_ttystate_err(-1), + m_ttystate(), + m_process_group(-1) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TTYState::~TTYState() +{ +} + +//---------------------------------------------------------------------- +// 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 +TTYState::Save (int fd, bool save_process_group) +{ + if (fd >= 0 && ::isatty (fd)) + { + m_fd = fd; + m_tflags = ::fcntl (fd, F_GETFL, 0); + m_ttystate_err = ::tcgetattr (fd, &m_ttystate); + if (save_process_group) + m_process_group = ::tcgetpgrp (0); + else + m_process_group = -1; + } + else + { + m_fd = -1; + m_tflags = -1; + m_ttystate_err = -1; + m_process_group = -1; + } + return m_ttystate_err == 0; +} + +//---------------------------------------------------------------------- +// Restore the state of the TTY using the cached values from a +// previous call to Save(). +//---------------------------------------------------------------------- +bool +TTYState::Restore () const +{ + int result = 0; + if (IsValid()) + { + if (TFlagsIsValid()) + result = fcntl (m_fd, F_SETFL, m_tflags); + + if (TTYStateIsValid()) + result = tcsetattr (m_fd, TCSANOW, &m_ttystate); + + 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 + result = tcsetpgrp (m_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 +TTYState::IsValid() const +{ + return (m_fd >= 0) && (TFlagsIsValid() || TTYStateIsValid()); +} + +//---------------------------------------------------------------------- +// Returns true if m_tflags is valid +//---------------------------------------------------------------------- +bool +TTYState::TFlagsIsValid() const +{ + return m_tflags != -1; +} + +//---------------------------------------------------------------------- +// Returns true if m_ttystate is valid +//---------------------------------------------------------------------- +bool +TTYState::TTYStateIsValid() const +{ + return m_ttystate_err == 0; +} + +//---------------------------------------------------------------------- +// Returns true if m_process_group is valid +//---------------------------------------------------------------------- +bool +TTYState::ProcessGroupIsValid() const +{ + return m_process_group != -1; +} + +//------------------------------------------------------------------ +// Constructor +//------------------------------------------------------------------ +TTYStateSwitcher::TTYStateSwitcher () : + m_currentState(UINT32_MAX) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +TTYStateSwitcher::~TTYStateSwitcher () +{ +} + +//------------------------------------------------------------------ +// Returns the number of states that this switcher contains +//------------------------------------------------------------------ +uint32_t +TTYStateSwitcher::GetNumberOfStates() const +{ + return sizeof(m_ttystates)/sizeof(TTYState); +} + +//------------------------------------------------------------------ +// Restore the state at index "idx". +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TTYStateSwitcher::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 +TTYStateSwitcher::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/lldb/source/Core/Timer.cpp b/lldb/source/Core/Timer.cpp new file mode 100644 index 000000000000..e0d3bab354a0 --- /dev/null +++ b/lldb/source/Core/Timer.cpp @@ -0,0 +1,235 @@ +//===-- Timer.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/Timer.h" + +#include +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 +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 CategoryMap; +static pthread_key_t g_key; + + +static Mutex & +GetCategoryMutex() +{ + static Mutex g_category_mutex(Mutex::eMutexTypeNormal); + return g_category_mutex; +} + +static CategoryMap & +GetCategoryMap() +{ + static CategoryMap 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::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) + { + // 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(); + bool notify = false; + if (m_total_start.IsValid()) + { + m_total_ticks += (stop_time - m_total_start); + m_total_start.Clear(); + notify = true; + } + 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; + ::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()); + CategoryMap &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 CategoryMap::const_iterator& lhs, const CategoryMap::const_iterator& rhs) +{ + return lhs->second > rhs->second; +} + + +void +Timer::ResetCategoryTimes () +{ + Mutex::Locker locker (GetCategoryMutex()); + CategoryMap &category_map = GetCategoryMap(); + category_map.clear(); +} + +void +Timer::DumpCategoryTimes (Stream *s) +{ + Mutex::Locker locker (GetCategoryMutex()); + CategoryMap &category_map = GetCategoryMap(); + std::vector sorted_iterators; + CategoryMap::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); + } +} \ No newline at end of file diff --git a/lldb/source/Core/UUID.cpp b/lldb/source/Core/UUID.cpp new file mode 100644 index 000000000000..1ccfdccfab07 --- /dev/null +++ b/lldb/source/Core/UUID.cpp @@ -0,0 +1,218 @@ +//===-- 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 +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +UUID::UUID() +{ + ::bzero (m_uuid, sizeof(m_uuid)); +} + +UUID::UUID(const UUID& rhs) +{ + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); +} + +UUID::UUID (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + if (uuid_bytes && num_uuid_bytes >= 16) + ::memcpy (m_uuid, uuid_bytes, sizeof (m_uuid)); + else + ::bzero (m_uuid, sizeof(m_uuid)); +} + +UUID::UUID (const uuid_t *uuid) +{ + if (uuid) + ::memcpy (m_uuid, uuid, sizeof (m_uuid)); + else + ::bzero (m_uuid, sizeof(m_uuid)); +} + +const UUID& +UUID::operator=(const UUID& rhs) +{ + if (this != &rhs) + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); + return *this; +} + +UUID::~UUID() +{ +} + +void +UUID::Clear() +{ + ::bzero (m_uuid, sizeof(m_uuid)); +} + +const void * +UUID::GetBytes() const +{ + return m_uuid; +} + +char * +UUID::GetAsCString (char *dst, size_t dst_len) const +{ + const uint8_t *u = (const uint8_t *)GetBytes(); + snprintf(dst, dst_len, "%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]); + return dst; +} + +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]); +} + +void +UUID::SetBytes (const void *uuid_bytes) +{ + if (uuid_bytes) + ::memcpy (m_uuid, uuid_bytes, sizeof (m_uuid)); + else + ::bzero (m_uuid, sizeof(m_uuid)); +} + +size_t +UUID::GetByteSize() +{ + return sizeof(UUID::ValueType); +} + +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]; +} + +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::SetfromCString (const char *cstr) +{ + if (cstr == NULL) + return 0; + + uint32_t uuid_byte_idx = 0; + const char *p = cstr; + + // Skip leading whitespace characters + while (isspace(*p)) + ++p; + + // Try and decode a UUID + while (*p != '\0') + { + if (isxdigit(*p) && 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 + m_uuid[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 == 16) + break; + } + else if (*p == '-') + { + // Skip dashes + p++; + } + else + { + // UUID values can only consist of hex characters and '-' chars + return 0; + } + } + // If we successfully decoded a UUID, return the amount of characters that + // were consumed + if (uuid_byte_idx == 16) + return p - cstr; + + // Else return zero to indicate we were not able to parse a UUID value + return 0; +} + + + +bool +lldb_private::operator == (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) == 0; +} + +bool +lldb_private::operator != (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) != 0; +} + +bool +lldb_private::operator < (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) < 0; +} + +bool +lldb_private::operator <= (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) <= 0; +} + +bool +lldb_private::operator > (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) > 0; +} + +bool +lldb_private::operator >= (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) >= 0; +} diff --git a/lldb/source/Core/UserID.cpp b/lldb/source/Core/UserID.cpp new file mode 100644 index 000000000000..b1dd5a618c12 --- /dev/null +++ b/lldb/source/Core/UserID.cpp @@ -0,0 +1,73 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +UserID::UserID (user_id_t uid) : + m_uid(uid) +{ +} + +UserID::~UserID () +{ +} + +void +UserID::Clear () +{ + m_uid = LLDB_INVALID_UID; +} + + +user_id_t +UserID::GetID () const +{ + return m_uid; +} + +void +UserID::SetID (user_id_t uid) +{ + m_uid = uid; +} + +UserID::IDMatches::IDMatches (user_id_t uid) : + m_uid(uid) +{ +} + +bool +UserID::IDMatches::operator() (const UserID& rhs) const +{ + return m_uid == rhs.GetID(); +} + +Stream& +lldb_private::operator << (Stream& strm, const UserID& uid) +{ + strm.Printf("{0x%8.8x}", uid.GetID()); + return strm; +} + +bool +lldb_private::operator== (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() == rhs.GetID(); +} + +bool +lldb_private::operator!= (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() != rhs.GetID(); +} + diff --git a/lldb/source/Core/VMRange.cpp b/lldb/source/Core/VMRange.cpp new file mode 100644 index 000000000000..2f5d8ecf9abb --- /dev/null +++ b/lldb/source/Core/VMRange.cpp @@ -0,0 +1,100 @@ +//===-- 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" + +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; +} + + +void +VMRange::Dump(Stream *s, lldb::addr_t offset) const +{ + s->AddressRange(offset + GetBaseAddress(), offset + GetEndAddress(), sizeof (addr_t)); +} + +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/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp new file mode 100644 index 000000000000..c4c17dd0428c --- /dev/null +++ b/lldb/source/Core/Value.cpp @@ -0,0 +1,803 @@ +//===-- 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/Stream.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" + +using namespace lldb; +using namespace lldb_private; + +Value::Value() : + m_value(), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(const Scalar& scalar) : + m_value(scalar), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(int v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(unsigned int v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(unsigned long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(long long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(unsigned long long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(float v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(double v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(long double v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(const uint8_t *bytes, int len) : + m_value(), + m_value_type(eValueTypeHostAddress), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ + 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_value_type(v.m_value_type), + m_context(v.m_context), + m_context_type(v.m_context_type) +{ + 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::CreateProxy() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->CreateProxy (); + + Value *ret = new Value; + ret->SetContext(eContextTypeValue, this); + return ret; +} + +Value * +Value::GetProxyTarget() +{ + if (m_context_type == eContextTypeValue) + return (Value*)m_context; + else + return NULL; +} + +//#include "clang/Lex/LiteralSupport.h" +//#include "clang/AST/ASTContext.h" +//#include "clang/Frontend/CompilerInstance.h" +// +//Value::Value (const char *data, llvm::CompilerInstance *compiler) +//{ +// clang::NumericLiteralParser parser(data, data + strlen(data), clang::SourceLocation(), +// compiler->getPreprocessor()); +// if (parser.had_error) +// { +// } +// else if (parser.isBool) +// { +// APInt value; +// parser.GetIntegerValue(value); +// } +// else if (parser.isLong) +// { +// } +// else if (parser.isLongLong) +// { +// } +// else if (parser.isFloat) +// { +// } +// +//} +// +void +Value::Dump (Stream* strm) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->Dump (strm); + return; + } + + 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)); +} + +void * +Value::GetOpaqueClangQualType() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetOpaqueClangQualType (); + + if (m_context_type == eContextTypeOpaqueClangQualType) + return m_context; + + return NULL; +} + +Value::ValueType +Value::GetValueType() const +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueType (); + + return m_value_type; +} + +lldb::AddressType +Value::GetValueAddressType () const +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueAddressType (); + + switch (m_value_type) + { + default: + case eValueTypeScalar: + break; + case eValueTypeLoadAddress: return eAddressTypeLoad; + case eValueTypeFileAddress: return eAddressTypeFile; + case eValueTypeHostAddress: return eAddressTypeHost; + } + return eAddressTypeInvalid; +} + + +Value::ContextType +Value::GetContextType() const +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetContextType (); + + return m_context_type; +} + +void +Value::SetValueType (Value::ValueType value_type) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->SetValueType(value_type); + return; + } + + m_value_type = value_type; +} + +void +Value::ClearContext () +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->ClearContext(); + return; + } + + m_context = NULL; + m_context_type = eContextTypeInvalid; +} + +void +Value::SetContext (Value::ContextType context_type, void *p) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->SetContext(context_type, p); + return; + } + + m_context_type = context_type; + m_context = p; +} + +RegisterInfo * +Value::GetRegisterInfo() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetRegisterInfo(); + + if (m_context_type == eContextTypeDCRegisterInfo) + return static_cast (m_context); + return NULL; +} + +Type * +Value::GetType() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetType(); + + if (m_context_type == eContextTypeDCType) + return static_cast (m_context); + return NULL; +} + +Scalar & +Value::GetScalar() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetScalar(); + + return m_value; +} + +void +Value::ResizeData(int len) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->ResizeData(len); + return; + } + + m_value_type = eValueTypeHostAddress; + m_data_buffer.SetByteSize(len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +bool +Value::ValueOf(ExecutionContext *exe_ctx, clang::ASTContext *ast_context) +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->ValueOf(exe_ctx, ast_context); + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + case eContextTypeOpaqueClangQualType: // clang::Type * + case eContextTypeDCRegisterInfo: // RegisterInfo * + case eContextTypeDCType: // Type * + break; + + case eContextTypeDCVariable: // Variable * + ResolveValue(exe_ctx, ast_context); + return true; + } + return false; +} + +size_t +Value::GetValueByteSize (clang::ASTContext *ast_context, Error *error_ptr) +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueByteSize(ast_context, error_ptr); + + size_t byte_size = 0; + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + // If we have no context, there is no way to know how much memory to read + if (error_ptr) + error_ptr->SetErrorString ("Invalid context type, there is no way to know how much memory to read."); + break; + + case eContextTypeOpaqueClangQualType: + if (ast_context == NULL) + { + if (error_ptr) + error_ptr->SetErrorString ("Can't determine size of opaque clang type with NULL ASTContext *."); + } + else + { + uint64_t bit_width = ClangASTContext::GetTypeBitSize (ast_context, m_context); + byte_size = (bit_width + 7 ) / 8; + } + break; + + case eContextTypeDCRegisterInfo: // RegisterInfo * + if (GetRegisterInfo()) + byte_size = GetRegisterInfo()->byte_size; + else if (error_ptr) + error_ptr->SetErrorString ("Can't determine byte size with NULL RegisterInfo *."); + + break; + + case eContextTypeDCType: // Type * + if (GetType()) + byte_size = GetType()->GetByteSize(); + else if (error_ptr) + error_ptr->SetErrorString ("Can't determine byte size with NULL Type *."); + break; + + case eContextTypeDCVariable: // Variable * + if (GetVariable()) + byte_size = GetVariable()->GetType()->GetByteSize(); + else if (error_ptr) + error_ptr->SetErrorString ("Can't determine byte size with NULL Variable *."); + 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; +} + +void * +Value::GetValueOpaqueClangQualType () +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueOpaqueClangQualType(); + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + break; + + case eContextTypeOpaqueClangQualType: + return m_context; + + case eContextTypeDCRegisterInfo: + break; // TODO: Eventually convert into a clang type? + + case eContextTypeDCType: + if (GetType()) + return GetType()->GetOpaqueClangQualType(); + break; + + case eContextTypeDCVariable: + if (GetVariable()) + return GetVariable()->GetType()->GetOpaqueClangQualType(); + break; + } + + return NULL; +} + +lldb::Format +Value::GetValueDefaultFormat () +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueDefaultFormat(); + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + break; + + case eContextTypeOpaqueClangQualType: + return Type::GetFormat (m_context); + + case eContextTypeDCRegisterInfo: + if (GetRegisterInfo()) + return GetRegisterInfo()->format; + break; + + case eContextTypeDCType: + if (GetType()) + return GetType()->GetFormat(); + break; + + case eContextTypeDCVariable: + if (GetVariable()) + return GetVariable()->GetType()->GetFormat(); + break; + + } + + // Return a good default in case we can't figure anything out + return eFormatHex; +} + +Error +Value::GetValueAsData (ExecutionContext *exe_ctx, clang::ASTContext *ast_context, DataExtractor &data, uint32_t data_offset) +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueAsData(exe_ctx, ast_context, data, data_offset); + + data.Clear(); + + Error error; + lldb::addr_t address = LLDB_INVALID_ADDRESS; + lldb::AddressType address_type = eAddressTypeFile; + switch (m_value_type) + { + default: + error.SetErrorStringWithFormat("Invalid value type %i.\n", m_value_type); + break; + + case eValueTypeScalar: + data.SetByteOrder (eByteOrderHost); + data.SetAddressByteSize(sizeof(void *)); + if (m_value.GetData (data)) + return error; // Success; + error.SetErrorStringWithFormat("Extracting data from value failed.\n"); + break; + + case eValueTypeLoadAddress: + if (exe_ctx == NULL) + { + error.SetErrorString ("Can't read memory (no execution context)."); + } + else if (exe_ctx->process == NULL) + { + error.SetErrorString ("Can't read memory (invalid process)."); + } + else + { + address = m_value.ULongLong(LLDB_INVALID_ADDRESS); + address_type = eAddressTypeLoad; + data.SetByteOrder(exe_ctx->process->GetByteOrder()); + data.SetAddressByteSize(exe_ctx->process->GetAddressByteSize()); + } + break; + + case eValueTypeFileAddress: + { + // 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 (GetVariable()) + { + lldb::addr_t file_addr = m_value.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()); + address = so_addr.GetLoadAddress (exe_ctx->process); + if (address != LLDB_INVALID_ADDRESS) + address_type = eAddressTypeLoad; + } + if (address_type == eAddressTypeFile) + error.SetErrorStringWithFormat ("%s is not loaded.\n", var_sc.module_sp->GetFileSpec().GetFilename().AsCString()); + } + else + { + error.SetErrorStringWithFormat ("Unable to resolve the module for file address 0x%llx for variable '%s'.\n", file_addr, variable->GetName().AsCString("")); + } + } + else + { + error.SetErrorString ("Invalid file 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); + data.SetByteOrder(eByteOrderHost); + data.SetAddressByteSize(sizeof(void *)); + address_type = eAddressTypeHost; + break; + } + + // Bail if we encountered any errors + if (error.Fail()) + return error; + + if (address == LLDB_INVALID_ADDRESS) + { + error.SetErrorStringWithFormat ("Invalid %s address.\n", address_type == eAddressTypeHost ? "host" : "load"); + return error; + } + + // If we got here, we need to read the value from memory + uint32_t byte_size = GetValueByteSize (ast_context, &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 = (uint8_t*)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) + { + if (exe_ctx->process->ReadMemory(address, dst, byte_size, error) != byte_size) + { + if (error.Success()) + error.SetErrorStringWithFormat("read %u bytes of memory from 0x%llx failed", (uint64_t)address, byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("Unsupported lldb::AddressType value (%i).\n", address_type); + } + } + else + { + error.SetErrorStringWithFormat ("Out of memory.\n"); + } + + return error; +} + +Scalar & +Value::ResolveValue(ExecutionContext *exe_ctx, clang::ASTContext *ast_context) +{ + Scalar scalar; + if (m_context_type == eContextTypeValue) + { + // Resolve the proxy + + Value * v = (Value*)m_context; + + m_value = v->m_value; + m_value_type = v->m_value_type; + m_context = v->m_context; + m_context_type = v->m_context_type; + + 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(); + } + } + + if (m_context_type == eContextTypeOpaqueClangQualType) + { + void *opaque_clang_qual_type = GetOpaqueClangQualType(); + switch (m_value_type) + { + case eValueTypeScalar: // raw scalar value + break; + + default: + case eValueTypeFileAddress: + m_value.Clear(); + break; + + case eValueTypeLoadAddress: // load address value + case eValueTypeHostAddress: // host address value (for memory in the process that is using liblldb) + { + lldb::AddressType address_type = m_value_type == eValueTypeLoadAddress ? eAddressTypeLoad : eAddressTypeHost; + lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS); + DataExtractor data; + if (Type::ReadFromMemory (exe_ctx, ast_context, opaque_clang_qual_type, addr, address_type, data)) + { + if (Type::GetValueAsScalar (ast_context, opaque_clang_qual_type, 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 == eContextTypeValue) + return ((Value*)m_context)->GetVariable(); + + if (m_context_type == eContextTypeDCVariable) + return static_cast (m_context); + return NULL; +} + + + +const char * +Value::GetValueTypeAsCString (ValueType value_type) +{ + switch (value_type) + { + case eValueTypeScalar: return "scalar"; + 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 eContextTypeOpaqueClangQualType: return "clang::Type *"; + case eContextTypeDCRegisterInfo: return "RegisterInfo *"; + case eContextTypeDCType: return "Type *"; + case eContextTypeDCVariable: 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/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp new file mode 100644 index 000000000000..93511febe8a5 --- /dev/null +++ b/lldb/source/Core/ValueObject.cpp @@ -0,0 +1,678 @@ +//===-- 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/Core/ValueObject.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "clang/AST/Type.h" +#include "llvm/Support/raw_ostream.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +static lldb::user_id_t g_value_obj_uid = 0; + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject () : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_update_id (0), // Value object lists always start at 1, value objects start at zero + m_name (), + m_data (), + m_value (), + m_error (), + m_flags (), + m_value_str(), + m_location_str(), + m_summary_str(), + m_children(), + m_synthetic_children() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ValueObject::~ValueObject () +{ +} + +user_id_t +ValueObject::GetUpdateID() const +{ + return m_update_id; +} + +bool +ValueObject::UpdateValueIfNeeded (ExecutionContextScope *exe_scope) +{ + if (exe_scope) + { + Process *process = exe_scope->CalculateProcess(); + if (process) + { + const user_id_t stop_id = process->GetStopID(); + if (m_update_id != stop_id) + { + m_value_str.clear(); + m_location_str.clear(); + m_summary_str.clear(); + + UpdateValue (exe_scope); + if (m_error.Success()) + m_update_id = stop_id; + } + } + } + return m_error.Success(); +} + +const DataExtractor & +ValueObject::GetDataExtractor () const +{ + return m_data; +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + return m_data; +} + +const Error & +ValueObject::GetError() const +{ + return m_error; +} + +const ConstString & +ValueObject::GetName() const +{ + return m_name; +} + +const char * +ValueObject::GetLocationAsCString (ExecutionContextScope *exe_scope) +{ + if (UpdateValueIfNeeded(exe_scope)) + { + if (m_location_str.empty()) + { + StreamString sstr; + + switch (m_value.GetValueType()) + { + default: + break; + + case Value::eValueTypeScalar: + if (m_value.GetContextType() == Value::eContextTypeDCRegisterInfo) + { + RegisterInfo *reg_info = m_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; + break; + } + } + m_location_str = "scalar"; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t addr_nibble_size = m_data.GetAddressByteSize() * 2; + sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size, m_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::GetValueIsValid () +{ + return m_flags.IsSet(eValueIsValid); +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + if (b) + m_flags.Set(eValueIsValid); + else + m_flags.Clear(eValueIsValid); +} + +bool +ValueObject::GetValueDidChange () const +{ + return m_flags.IsSet(eValueChanged); +} + +void +ValueObject::SetValueDidChange (bool value_changed) +{ + m_flags.Set(eValueChanged); +} + +ValueObjectSP +ValueObject::GetChildAtIndex (uint32_t idx, bool can_create) +{ + ValueObjectSP child_sp; + if (idx < GetNumChildren()) + { + // Check if we have already made the child value object? + if (can_create && m_children[idx].get() == NULL) + { + // 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[idx] = CreateChildAtIndex (idx, false, 0); + } + + child_sp = m_children[idx]; + } + return child_sp; +} + +uint32_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return ClangASTContext::GetIndexOfChildWithName (GetClangAST(), + GetOpaqueClangQualType(), + name.AsCString(), + omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + // when getting a child by name, it could be burried 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 + std::vector child_indexes; + clang::ASTContext *clang_ast = GetClangAST(); + void *clang_type = GetOpaqueClangQualType(); + bool omit_empty_base_classes = true; + const size_t num_child_indexes = ClangASTContext::GetIndexOfChildMemberWithName (clang_ast, + clang_type, + name.AsCString(), + omit_empty_base_classes, + child_indexes); + ValueObjectSP child_sp; + 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; +} + + +uint32_t +ValueObject::GetNumChildren () +{ + if (m_flags.IsClear(eNumChildrenHasBeenSet)) + { + SetNumChildren (CalculateNumChildren()); + } + return m_children.size(); +} +void +ValueObject::SetNumChildren (uint32_t num_children) +{ + m_flags.Set(eNumChildrenHasBeenSet); + m_children.resize(num_children); +} + +void +ValueObject::SetName (const char *name) +{ + m_name.SetCString(name); +} + +void +ValueObject::SetName (const ConstString &name) +{ + m_name = name; +} + +ValueObjectSP +ValueObject::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + bool omit_empty_base_classes = true; + + 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; + const bool transparent_pointers = synthetic_array_member == false; + clang::ASTContext *clang_ast = GetClangAST(); + void *clang_type = GetOpaqueClangQualType(); + void *child_clang_type; + child_clang_type = ClangASTContext::GetChildClangTypeAtIndex (clang_ast, + GetName().AsCString(), + clang_type, + idx, + transparent_pointers, + omit_empty_base_classes, + child_name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + 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_sp.reset (new ValueObjectChild (this, + clang_ast, + child_clang_type, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset)); + } + return valobj_sp; +} + +const char * +ValueObject::GetSummaryAsCString (ExecutionContextScope *exe_scope) +{ + if (UpdateValueIfNeeded (exe_scope)) + { + if (m_summary_str.empty()) + { + void *clang_type = GetOpaqueClangQualType(); + + // See if this is a pointer to a C string? + uint32_t fixed_length = 0; + if (clang_type && ClangASTContext::IsCStringType (clang_type, fixed_length)) + { + Process *process = exe_scope->CalculateProcess(); + if (process != NULL) + { + StreamString sstr; + lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS; + lldb::AddressType cstr_address_type = eAddressTypeInvalid; + switch (GetValue().GetValueType()) + { + case Value::eValueTypeScalar: + cstr_address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + cstr_address_type = eAddressTypeLoad; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t data_offset = 0; + cstr_address = m_data.GetPointer(&data_offset); + cstr_address_type = m_value.GetValueAddressType(); + if (cstr_address_type == eAddressTypeInvalid) + cstr_address_type = eAddressTypeLoad; + } + break; + } + + if (cstr_address != LLDB_INVALID_ADDRESS) + { + DataExtractor data; + size_t bytes_read = 0; + std::vector data_buffer; + std::vector cstr_buffer; + size_t cstr_length; + Error error; + if (fixed_length > 0) + { + data_buffer.resize(fixed_length); + // Resize the formatted buffer in case every character + // uses the "\xXX" format and one extra byte for a NULL + cstr_buffer.resize(data_buffer.size() * 4 + 1); + data.SetData (data_buffer.data(), data_buffer.size(), eByteOrderHost); + bytes_read = process->ReadMemory (cstr_address, data_buffer.data(), fixed_length, error); + if (bytes_read > 0) + { + sstr << '"'; + cstr_length = data.Dump (&sstr, + 0, // Start offset in "data" + eFormatChar, // Print as characters + 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 + sstr << '"'; + } + } + else + { + const size_t k_max_buf_size = 256; + data_buffer.resize (k_max_buf_size + 1); + // NULL terminate in case we don't get the entire C string + data_buffer.back() = '\0'; + // Make a formatted buffer that can contain take 4 + // bytes per character in case each byte uses the + // "\xXX" format and one extra byte for a NULL + cstr_buffer.resize (k_max_buf_size * 4 + 1); + + data.SetData (data_buffer.data(), data_buffer.size(), eByteOrderHost); + size_t total_cstr_len = 0; + while ((bytes_read = process->ReadMemory (cstr_address, data_buffer.data(), k_max_buf_size, error)) > 0) + { + size_t len = strlen(data_buffer.data()); + if (len == 0) + break; + if (len > bytes_read) + len = bytes_read; + if (sstr.GetSize() == 0) + sstr << '"'; + + cstr_length = data.Dump (&sstr, + 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 + + if (len < k_max_buf_size) + break; + cstr_address += total_cstr_len; + } + if (sstr.GetSize() > 0) + sstr << '"'; + } + + if (sstr.GetSize() > 0) + m_summary_str.assign (sstr.GetData(), sstr.GetSize()); + } + } + } + } + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + + +const char * +ValueObject::GetValueAsCString (ExecutionContextScope *exe_scope) +{ + // If our byte size is zero this is an aggregate type that has children + if (ClangASTContext::IsAggregateType (GetOpaqueClangQualType()) == false) + { + if (UpdateValueIfNeeded(exe_scope)) + { + if (m_value_str.empty()) + { + const Value::ContextType context_type = m_value.GetContextType(); + + switch (context_type) + { + case Value::eContextTypeOpaqueClangQualType: + case Value::eContextTypeDCType: + case Value::eContextTypeDCVariable: + { + void *clang_type = GetOpaqueClangQualType (); + if (clang_type) + { + StreamString sstr; + lldb::Format format = Type::GetFormat(clang_type); + if (Type::DumpTypeValue(&sstr, + GetClangAST(), // The clang AST + clang_type, // The clang type to display + format, // Format to display this type with + 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 + m_value_str.swap(sstr.GetString()); + else + m_value_str.clear(); + } + } + break; + + case Value::eContextTypeDCRegisterInfo: + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + StreamString reg_sstr; + m_data.Dump(®_sstr, 0, reg_info->format, reg_info->byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + m_value_str.swap(reg_sstr.GetString()); + } + } + break; + } + } + } + } + if (m_value_str.empty()) + return NULL; + return m_value_str.c_str(); +} + +bool +ValueObject::SetValueFromCString (ExecutionContextScope *exe_scope, const char *value_str) +{ + // Make sure our value is up to date first so that our location and location + // type is valid. + if (!UpdateValueIfNeeded(exe_scope)) + return false; + + uint32_t count = 0; + lldb::Encoding encoding = Type::GetEncoding (GetOpaqueClangQualType(), count); + + char *end = NULL; + size_t byte_size = GetByteSize(); + switch (encoding) + { + case eEncodingInvalid: + return false; + + case eEncodingUint: + if (byte_size > sizeof(unsigned long long)) + { + return false; + } + else + { + unsigned long long ull_val = strtoull(value_str, &end, 0); + if (end && *end != '\0') + return false; + m_value = ull_val; + // Limit the bytes in our m_data appropriately. + m_value.GetScalar().GetData (m_data, byte_size); + } + break; + + case eEncodingSint: + if (byte_size > sizeof(long long)) + { + return false; + } + else + { + long long sll_val = strtoll(value_str, &end, 0); + if (end && *end != '\0') + return false; + m_value = sll_val; + // Limit the bytes in our m_data appropriately. + m_value.GetScalar().GetData (m_data, byte_size); + } + break; + + case eEncodingIEEE754: + { + const size_t byte_size = GetByteSize(); + const off_t byte_offset = GetByteOffset(); + uint8_t *dst = (uint8_t *)m_data.PeekData(byte_offset, byte_size); + if (dst != NULL) + { + // We are decoding a float into host byte order below, so make + // sure m_data knows what it contains. + m_data.SetByteOrder(eByteOrderHost); + const size_t converted_byte_size = ClangASTContext::ConvertStringToFloatValue ( + GetClangAST(), + GetOpaqueClangQualType(), + value_str, + dst, + byte_size); + + if (converted_byte_size == byte_size) + { + } + } + } + break; + + case eEncodingVector: + return false; + + default: + return false; + } + + // If we have made it here the value is in m_data and we should write it + // out to the target + return Write (); +} + +bool +ValueObject::Write () +{ + // Clear the update ID so the next time we try and read the value + // we try and read it again. + m_update_id = 0; + + // TODO: when Value has a method to write a value back, call it from here. + return false; + +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObjectSP& valobj_sp) +{ + m_synthetic_children[key] = valobj_sp; +} + +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; + return synthetic_child_sp; +} + +bool +ValueObject::IsPointerType () +{ + return ClangASTContext::IsPointerType (GetOpaqueClangQualType()); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return ClangASTContext::IsPointerOrReferenceType(GetOpaqueClangQualType()); +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%i]", 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) + { + // We haven't made a synthetic array member for INDEX yet, so + // lets make one and cache it for any future reference. + synthetic_child_sp = CreateChildAtIndex(0, true, index); + + // Cache the value if we got one back... + if (synthetic_child_sp) + AddSyntheticChild(index_const_str, synthetic_child_sp); + } + } + return synthetic_child_sp; +} diff --git a/lldb/source/Core/ValueObjectChild.cpp b/lldb/source/Core/ValueObjectChild.cpp new file mode 100644 index 000000000000..dc10bb09b4cc --- /dev/null +++ b/lldb/source/Core/ValueObjectChild.cpp @@ -0,0 +1,207 @@ +//===-- 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/dwarf.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.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, + clang::ASTContext *clang_ast, + void *clang_type, + const ConstString &name, + uint32_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset +) : + ValueObject (), + m_parent (parent), + m_clang_ast (clang_ast), + 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_name = name; +} + +ValueObjectChild::~ValueObjectChild() +{ +} + +void * +ValueObjectChild::GetOpaqueClangQualType() +{ + return m_clang_type; +} + +lldb::ValueType +ValueObjectChild::GetValueType() const +{ + return m_parent->GetValueType(); +} + +uint32_t +ValueObjectChild::CalculateNumChildren() +{ + return ClangASTContext::GetNumChildren (m_clang_type, true); +} + +clang::ASTContext * +ValueObjectChild::GetClangAST () +{ + return m_clang_ast; +} + +size_t +ValueObjectChild::GetByteSize() +{ + return m_byte_size; +} + +off_t +ValueObjectChild::GetByteOffset() +{ + return m_byte_offset; +} + +uint32_t +ValueObjectChild::GetBitfieldBitSize() +{ + return m_bitfield_bit_size; +} + +uint32_t +ValueObjectChild::GetBitfieldBitOffset() +{ + return m_bitfield_bit_offset; +} + +ConstString +ValueObjectChild::GetTypeName() +{ + if (m_type_name.IsEmpty()) + { + m_type_name = Type::GetClangTypeName (GetOpaqueClangQualType()); + if (m_type_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = m_type_name.AsCString(); + if (clang_type_name) + { + char bitfield_type_name[strlen(clang_type_name) + 32]; + ::snprintf (bitfield_type_name, sizeof(bitfield_type_name), "%s:%u", clang_type_name, m_bitfield_bit_size); + m_type_name.SetCString(bitfield_type_name); + } + } + } + } + return m_type_name; +} + +void +ValueObjectChild::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + SetValueIsValid (false); + ValueObject* parent = m_parent; + if (parent) + { + if (parent->UpdateValueIfNeeded(exe_scope)) + { + m_value.SetContext(Value::eContextTypeOpaqueClangQualType, m_clang_type); + + // 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 (ClangASTContext::IsPointerOrReferenceType (parent->GetOpaqueClangQualType())) + { + uint32_t offset = 0; + m_value.GetScalar() = parent->GetDataExtractor().GetPointer(&offset); + // For pointers, m_byte_offset should only ever be set if we + // ValueObject::GetSyntheticArrayMemberFromPointer() was called + if (ClangASTContext::IsPointerType (parent->GetOpaqueClangQualType()) && m_byte_offset) + m_value.GetScalar() += m_byte_offset; + if (value_type == Value::eValueTypeScalar || + value_type == Value::eValueTypeFileAddress) + m_value.SetValueType (Value::eValueTypeLoadAddress); + } + 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 || addr == 0) + { + m_error.SetErrorStringWithFormat("Parent address is invalid: 0x%llx.\n", addr); + break; + } + // Set this object's scalar value to the address of its + // value be 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 (exe_scope); + m_error = m_value.GetValueAsData (&exe_ctx, GetClangAST (), m_data, 0); + } + } + else + { + m_error.SetErrorStringWithFormat("Parent failed to evaluate: %s.\n", parent->GetError().AsCString()); + } + } + else + { + m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject."); + } +} + + +bool +ValueObjectChild::IsInScope (StackFrame *frame) +{ + return m_parent->IsInScope (frame); +} + diff --git a/lldb/source/Core/ValueObjectList.cpp b/lldb/source/Core/ValueObjectList.cpp new file mode 100644 index 000000000000..50ae1de0985a --- /dev/null +++ b/lldb/source/Core/ValueObjectList.cpp @@ -0,0 +1,119 @@ +//===-- 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); +} + +uint32_t +ValueObjectList::GetSize() const +{ + return m_value_objects.size(); +} + +lldb::ValueObjectSP +ValueObjectList::GetValueObjectAtIndex (uint32_t idx) +{ + lldb::ValueObjectSP valobj_sp; + if (idx < m_value_objects.size()) + valobj_sp = m_value_objects[idx]; + return 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) + { + if ((*pos)->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) + { + if ((*pos)->GetID() == uid) + { + valobj_sp = *pos; + break; + } + } + return valobj_sp; +} + + +ValueObjectSP +ValueObjectList::FindValueObjectByPointer (ValueObject *valobj) +{ + ValueObjectSP valobj_sp; + collection::iterator pos, end = m_value_objects.end(); + + for (pos = m_value_objects.begin(); pos != end; ++pos) + { + if ((*pos).get() == valobj) + { + valobj_sp = *pos; + break; + } + } + return valobj_sp; +} diff --git a/lldb/source/Core/ValueObjectRegister.cpp b/lldb/source/Core/ValueObjectRegister.cpp new file mode 100644 index 000000000000..aae818eadd63 --- /dev/null +++ b/lldb/source/Core/ValueObjectRegister.cpp @@ -0,0 +1,331 @@ +//===-- 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/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 (RegisterContext *reg_ctx) : + ValueObject (), + m_reg_ctx (reg_ctx) +{ + assert (reg_ctx); + m_name.SetCString("Registers"); + SetValueIsValid (true); +} + +ValueObjectRegisterContext::~ValueObjectRegisterContext() +{ +} + +void * +ValueObjectRegisterContext::GetOpaqueClangQualType () +{ + return NULL; +} + +ConstString +ValueObjectRegisterContext::GetTypeName() +{ + ConstString empty_type_name; + return empty_type_name; +} + +uint32_t +ValueObjectRegisterContext::CalculateNumChildren() +{ + return m_reg_ctx->GetRegisterSetCount(); +} + +clang::ASTContext * +ValueObjectRegisterContext::GetClangAST () +{ + return NULL; +} + +size_t +ValueObjectRegisterContext::GetByteSize() +{ + return 0; +} + +void +ValueObjectRegisterContext::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + StackFrame *frame = exe_scope->CalculateStackFrame(); + if (frame) + m_reg_ctx = frame->GetRegisterContext(); + else + m_reg_ctx = NULL; + + SetValueIsValid (m_reg_ctx != NULL); +} + +ValueObjectSP +ValueObjectRegisterContext::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + + const uint32_t num_children = GetNumChildren(); + if (idx < num_children) + valobj_sp.reset (new ValueObjectRegisterSet(m_reg_ctx, idx)); + return valobj_sp; +} + + +#pragma mark - +#pragma mark ValueObjectRegisterSet + +ValueObjectRegisterSet::ValueObjectRegisterSet (RegisterContext *reg_ctx, uint32_t reg_set_idx) : + ValueObject (), + m_reg_ctx (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() +{ +} + +void * +ValueObjectRegisterSet::GetOpaqueClangQualType () +{ + return NULL; +} + +ConstString +ValueObjectRegisterSet::GetTypeName() +{ + return ConstString(); +} + +uint32_t +ValueObjectRegisterSet::CalculateNumChildren() +{ + const RegisterSet *reg_set = m_reg_ctx->GetRegisterSet(m_reg_set_idx); + if (reg_set) + return reg_set->num_registers; + return 0; +} + +clang::ASTContext * +ValueObjectRegisterSet::GetClangAST () +{ + return NULL; +} + +size_t +ValueObjectRegisterSet::GetByteSize() +{ + return 0; +} + +void +ValueObjectRegisterSet::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + SetValueDidChange (false); + StackFrame *frame = exe_scope->CalculateStackFrame(); + if (frame == NULL) + m_reg_ctx = NULL; + else + { + m_reg_ctx = frame->GetRegisterContext (); + if (m_reg_ctx) + { + const RegisterSet *reg_set = m_reg_ctx->GetRegisterSet (m_reg_set_idx); + if (reg_set == NULL) + m_reg_ctx = NULL; + else if (m_reg_set != reg_set) + { + SetValueDidChange (true); + m_name.SetCString(reg_set->name); + } + } + } + if (m_reg_ctx) + { + SetValueIsValid (true); + } + else + { + SetValueIsValid (false); + m_children.clear(); + } +} + + +ValueObjectSP +ValueObjectRegisterSet::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + if (m_reg_ctx && m_reg_set) + { + const uint32_t num_children = GetNumChildren(); + if (idx < num_children) + valobj_sp.reset (new ValueObjectRegister(m_reg_ctx, m_reg_set->registers[idx])); + } + return valobj_sp; +} + + +#pragma mark - +#pragma mark ValueObjectRegister + +ValueObjectRegister::ValueObjectRegister (RegisterContext *reg_ctx, uint32_t reg_num) : + ValueObject (), + m_reg_ctx (reg_ctx), + m_reg_info (NULL), + m_reg_num (reg_num), + m_type_name (), + m_clang_type (NULL) +{ + assert (reg_ctx); + m_reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num); + if (m_reg_info) + { + if (m_reg_info->name) + m_name.SetCString(m_reg_info->name); + else if (m_reg_info->alt_name) + m_name.SetCString(m_reg_info->alt_name); + } +} + +ValueObjectRegister::~ValueObjectRegister() +{ +} + +void * +ValueObjectRegister::GetOpaqueClangQualType () +{ + if (m_clang_type == NULL && m_reg_info) + { + Process *process = m_reg_ctx->CalculateProcess (); + if (process) + { + Module *exe_module = process->GetTarget().GetExecutableModule ().get(); + if (exe_module) + { + TypeList *type_list = exe_module->GetTypeList(); + if (type_list) + m_clang_type = type_list->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 = Type::GetClangTypeName (GetOpaqueClangQualType()); + return m_type_name; +} + +uint32_t +ValueObjectRegister::CalculateNumChildren() +{ + return 0; +} + +clang::ASTContext * +ValueObjectRegister::GetClangAST () +{ + Process *process = m_reg_ctx->CalculateProcess (); + if (process) + { + Module *exe_module = process->GetTarget().GetExecutableModule ().get(); + if (exe_module) + { + TypeList *type_list = exe_module->GetTypeList(); + if (type_list) + return type_list->GetClangASTContext().getASTContext(); + } + } + return NULL; +} + +size_t +ValueObjectRegister::GetByteSize() +{ + return m_reg_info->byte_size; +} + +void +ValueObjectRegister::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + StackFrame *frame = exe_scope->CalculateStackFrame(); + if (frame) + { + m_reg_ctx = frame->GetRegisterContext(); + if (m_reg_ctx) + { + const RegisterInfo *reg_info = m_reg_ctx->GetRegisterInfoAtIndex(m_reg_num); + if (m_reg_info != reg_info) + { + m_reg_info = reg_info; + if (m_reg_info) + { + if (m_reg_info->name) + m_name.SetCString(m_reg_info->name); + else if (m_reg_info->alt_name) + m_name.SetCString(m_reg_info->alt_name); + } + } + } + } + else + { + m_reg_ctx = NULL; + m_reg_info = NULL; + } + + + if (m_reg_ctx && m_reg_info) + { + if (m_reg_ctx->ReadRegisterBytes (m_reg_num, m_data)) + { + m_value.SetContext(Value::eContextTypeDCRegisterInfo, (void *)m_reg_info); + m_value.SetValueType(Value::eValueTypeHostAddress); + m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); + SetValueIsValid (true); + return; + } + } + SetValueIsValid (false); +} + + diff --git a/lldb/source/Core/ValueObjectVariable.cpp b/lldb/source/Core/ValueObjectVariable.cpp new file mode 100644 index 000000000000..0b94535387f0 --- /dev/null +++ b/lldb/source/Core/ValueObjectVariable.cpp @@ -0,0 +1,167 @@ +//===-- 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/ValueObjectList.h" +#include "lldb/Core/Value.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" + +#include "clang/AST/Type.h" + + +using namespace lldb_private; + +ValueObjectVariable::ValueObjectVariable (lldb::VariableSP &var_sp) : + ValueObject(), + 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() +{ +} + +void * +ValueObjectVariable::GetOpaqueClangQualType () +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetOpaqueClangQualType(); + return NULL; +} + +ConstString +ValueObjectVariable::GetTypeName() +{ + Type * var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetName(); + ConstString empty_type_name; + return empty_type_name; +} + +uint32_t +ValueObjectVariable::CalculateNumChildren() +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetNumChildren(true); + return 0; +} + +clang::ASTContext * +ValueObjectVariable::GetClangAST () +{ + return m_variable_sp->GetType()->GetClangAST(); +} + +size_t +ValueObjectVariable::GetByteSize() +{ + return m_variable_sp->GetType()->GetByteSize(); +} + +lldb::ValueType +ValueObjectVariable::GetValueType() const +{ + if (m_variable_sp) + return m_variable_sp->GetScope(); + return lldb::eValueTypeInvalid; +} + + + +void +ValueObjectVariable::UpdateValue (ExecutionContextScope *exe_scope) +{ + SetValueIsValid (false); + m_error.Clear(); + + Variable *variable = m_variable_sp.get(); + DWARFExpression &expr = variable->LocationExpression(); + Value old_value(m_value); + ExecutionContext exe_ctx (exe_scope); + if (expr.Evaluate (&exe_ctx, GetClangAST(), NULL, m_value, &m_error)) + { + m_value.SetContext(Value::eContextTypeDCVariable, variable); + + 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, GetClangAST(), m_data, 0); + 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 (ClangASTContext::IsAggregateType (GetOpaqueClangQualType())) + { + // 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::eContextTypeDCVariable, variable); + m_error = value.GetValueAsData(&exe_ctx, GetClangAST(), m_data, 0); + } + break; + } + + SetValueIsValid (m_error.Success()); + } +} + + + +bool +ValueObjectVariable::IsInScope (StackFrame *frame) +{ + return m_variable_sp->IsInScope (frame); +} + diff --git a/lldb/source/Expression/ClangASTSource.cpp b/lldb/source/Expression/ClangASTSource.cpp new file mode 100644 index 000000000000..996c20529ac9 --- /dev/null +++ b/lldb/source/Expression/ClangASTSource.cpp @@ -0,0 +1,101 @@ +/* + * ClangASTSource.cpp + * lldb + * + * Created by John McCall on 6/1/10. + * Copyright 2010 Apple. All rights reserved. + * + */ + +#define NO_RTTI + +#include "clang/AST/ASTContext.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" + +using namespace clang; +using namespace lldb_private; + +ClangASTSource::~ClangASTSource() {} + +void ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) { + // Tell Sema to ask us when looking into the translation unit's decl. + Context.getTranslationUnitDecl()->setHasExternalVisibleStorage(); + Context.getTranslationUnitDecl()->setHasExternalLexicalStorage(); +} + +// These are only required for AST source that want to lazily load +// the declarations (or parts thereof) that they return. +Decl *ClangASTSource::GetExternalDecl(uint32_t) { return 0; } +Stmt *ClangASTSource::GetExternalDeclStmt(uint64_t) { return 0; } + +// These are also optional, although it might help with ObjC +// debugging if we have respectable signatures. But a more +// efficient interface (that didn't require scanning all files +// for method signatures!) might help. +Selector ClangASTSource::GetExternalSelector(uint32_t) { return Selector(); } +uint32_t ClangASTSource::GetNumExternalSelectors() { return 0; } + +// The core lookup interface. +DeclContext::lookup_result ClangASTSource::FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) { + switch (Name.getNameKind()) { + // Normal identifiers. + case DeclarationName::Identifier: + break; + + // Operator names. Not important for now. + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXLiteralOperatorName: + return DeclContext::lookup_result(); + + // 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: + return SetNoExternalVisibleDeclsForName(DC, Name); + + // These aren't looked up like this. + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + return DeclContext::lookup_result(); + + // These aren't possible in the global context. + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + return DeclContext::lookup_result(); + } + + llvm::SmallVector Decls; + + NameSearchContext NSC(*this, Decls, Name, DC); + + DeclMap.GetDecls(NSC, Name.getAsString().c_str()); + return SetExternalVisibleDeclsForName(DC, Name, Decls); +} + +// This is used to support iterating through an entire lexical context, +// which isn't something the debugger should ever need to do. +bool ClangASTSource::FindExternalLexicalDecls(const DeclContext *DC, llvm::SmallVectorImpl &Decls) { + // true is for error, that's good enough for me + return true; +} + +clang::ASTContext *NameSearchContext::GetASTContext() { + return &ASTSource.Context; +} + +clang::NamedDecl *NameSearchContext::AddVarDecl(void *type) { + clang::NamedDecl *Decl = VarDecl::Create(ASTSource.Context, + const_cast(DC), + SourceLocation(), + Name.getAsIdentifierInfo(), + QualType::getFromOpaquePtr(type), + 0, + VarDecl::Static, + VarDecl::Static); + Decls.push_back(Decl); + + return Decl; +} diff --git a/lldb/source/Expression/ClangExpression.cpp b/lldb/source/Expression/ClangExpression.cpp new file mode 100644 index 000000000000..f7742c685c70 --- /dev/null +++ b/lldb/source/Expression/ClangExpression.cpp @@ -0,0 +1,633 @@ +//===-- ClangExpression.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 + +// Other libraries and framework includes +#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/ModuleBuilder.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Driver/OptTable.h" +#include "clang/Frontend/CodeGenAction.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/Frontend/VerifyDiagnosticsClient.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/ParseAST.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/Module.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/LLVMContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/DynamicLibrary.h" +#include "llvm/System/Host.h" +#include "llvm/System/Signals.h" +#include "llvm/Target/TargetSelect.h" + +// Project includes +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangStmtVisitor.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Expression/RecordingMemoryManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +#define NO_RTTI +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Core/dwarf.h" + + +using namespace lldb_private; +using namespace clang; +using namespace llvm; + +namespace clang { + +class AnalyzerOptions; +class CodeGenOptions; +class DependencyOutputOptions; +class DiagnosticOptions; +class FrontendOptions; +class HeaderSearchOptions; +class LangOptions; +class PreprocessorOptions; +class PreprocessorOutputOptions; +class TargetInfo; +class TargetOptions; + +} // end namespace clang + + + +//===----------------------------------------------------------------------===// +// Utility Methods +//===----------------------------------------------------------------------===// + +std::string GetBuiltinIncludePath(const char *Argv0) { + llvm::sys::Path P = + llvm::sys::Path::GetMainExecutable(Argv0, + (void*)(intptr_t) GetBuiltinIncludePath); + + if (!P.isEmpty()) { + P.eraseComponent(); // Remove /clang from foo/bin/clang + P.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang//include + P.appendComponent("lib"); + P.appendComponent("clang"); + P.appendComponent(CLANG_VERSION_STRING); + P.appendComponent("include"); + } + + return P.str(); +} + + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +void LLVMErrorHandler(void *UserData, const std::string &Message) { + Diagnostic &Diags = *static_cast(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + exit(1); +} + +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 ASTPrintXML: return new ASTPrintXMLAction(); + 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 EmitObj: return new EmitObjAction(); + case FixIt: return new FixItAction(); + case GeneratePCH: return new GeneratePCHAction(); + case GeneratePTH: return new GeneratePTHAction(); + case InheritanceView: return new InheritanceViewAction(); + case InitOnly: return new InitOnlyAction(); + case ParseNoop: return new ParseOnlyAction(); + case ParsePrintCallbacks: return new PrintParseAction(); + case ParseSyntaxOnly: return new SyntaxOnlyAction(); + + case PluginAction: { + if (CI.getFrontendOpts().ActionName == "help") { + llvm::errs() << "clang -cc1 plugins:\n"; + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), + ie = FrontendPluginRegistry::end(); + it != ie; ++it) + llvm::errs() << " " << it->getName() << " - " << it->getDesc() << "\n"; + return 0; + } + + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == CI.getFrontendOpts().ActionName) + return it->instantiate(); + } + + CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) + << CI.getFrontendOpts().ActionName; + return 0; + } + + case PrintDeclContext: return new DeclContextPrintAction(); + 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(); + } +} + +//---------------------------------------------------------------------- +// ClangExpression constructor +//---------------------------------------------------------------------- +ClangExpression::ClangExpression(const char *target_triple, + ClangExpressionDeclMap *decl_map) : + m_target_triple (), + m_jit_mm_ptr (NULL), + m_code_generator_ptr (NULL), + m_decl_map (decl_map) +{ + if (target_triple && target_triple[0]) + m_target_triple = target_triple; + else + m_target_triple = llvm::sys::getHostTriple(); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangExpression::~ClangExpression() +{ + if (m_code_generator_ptr && !m_execution_engine.get()) + delete m_code_generator_ptr; +} + +bool +ClangExpression::CreateCompilerInstance (bool &IsAST) +{ + // Initialize targets first, so that --version shows registered targets. + static struct InitializeLLVM { + InitializeLLVM() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + } + } InitializeLLVM; + + // 1. Create a new compiler instance. + m_clang_ap.reset(new CompilerInstance()); + m_clang_ap->setLLVMContext(new LLVMContext()); + + // 2. Set options. + + // Parse expressions as Objective C++ regardless of context. + // Our hook into Clang's lookup mechanism only works in C++. + m_clang_ap->getLangOpts().CPlusPlus = true; + m_clang_ap->getLangOpts().ObjC1 = true; + + // Disable some warnings. + m_clang_ap->getDiagnosticOpts().Warnings.push_back("no-unused-value"); + + // Set the target triple. + m_clang_ap->getTargetOpts().Triple = m_target_triple; + + // 3. Set up various important bits of infrastructure. + + m_clang_ap->createDiagnostics(0, 0); + m_clang_ap->getLangOpts().CPlusPlus = true; + + // Create the target instance. + m_clang_ap->setTarget(TargetInfo::CreateTargetInfo(m_clang_ap->getDiagnostics(), + m_clang_ap->getTargetOpts())); + if (!m_clang_ap->hasTarget()) + { + m_clang_ap.reset(); + return false; + } + + // 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_clang_ap->getTarget().setForcedLangOptions(m_clang_ap->getLangOpts()); + + return m_clang_ap.get(); +} + +Mutex & +ClangExpression::GetClangMutex () +{ + static Mutex g_clang_mutex(Mutex::eMutexTypeRecursive); // Control access to the clang compiler + return g_clang_mutex; +} + + +clang::ASTContext * +ClangExpression::GetASTContext () +{ + CompilerInstance *compiler_instance = GetCompilerInstance(); + if (compiler_instance) + return &compiler_instance->getASTContext(); + return NULL; +} + +unsigned +ClangExpression::ParseExpression (const char *expr_text, Stream &stream) +{ + // HACK: for now we have to make a function body around our expression + // since there is no way to parse a single expression line in LLVM/Clang. + std::string func_expr("void ___clang_expr()\n{\n\t"); + func_expr.append(expr_text); + func_expr.append(";\n}"); + return ParseBareExpression (func_expr, stream); + +} + +unsigned +ClangExpression::ParseBareExpression (llvm::StringRef expr_text, Stream &stream) +{ + Mutex::Locker locker(GetClangMutex ()); + + TextDiagnosticBuffer text_diagnostic_buffer; + + bool IsAST = false; + if (!CreateCompilerInstance (IsAST)) + { + stream.Printf("error: couldn't create compiler instance\n"); + return 1; + } + + // This code is matched below by a setClient to NULL. + // We cannot return out of this code without doing that. + m_clang_ap->getDiagnostics().setClient(&text_diagnostic_buffer); + text_diagnostic_buffer.FlushDiagnostics (m_clang_ap->getDiagnostics()); + + MemoryBuffer *memory_buffer = MemoryBuffer::getMemBufferCopy(expr_text, __FUNCTION__); + + if (!m_clang_ap->hasSourceManager()) + m_clang_ap->createSourceManager(); + + m_clang_ap->createFileManager(); + m_clang_ap->createPreprocessor(); + + // Build the ASTContext. Most of this we inherit from the + // CompilerInstance, but we also want to give the context + // an ExternalASTSource. + SelectorTable selector_table; + std::auto_ptr builtin_ap(new Builtin::Context(m_clang_ap->getTarget())); + ASTContext *Context = new ASTContext(m_clang_ap->getLangOpts(), + m_clang_ap->getSourceManager(), + m_clang_ap->getTarget(), + m_clang_ap->getPreprocessor().getIdentifierTable(), + selector_table, + *builtin_ap.get()); + + llvm::OwningPtr ASTSource(new ClangASTSource(*Context, *m_decl_map)); + + if (m_decl_map) + { + Context->setExternalSource(ASTSource); + } + + m_clang_ap->setASTContext(Context); + + FileID memory_buffer_file_id = m_clang_ap->getSourceManager().createMainFileIDForMemBuffer (memory_buffer); + std::string module_name("test_func"); + text_diagnostic_buffer.BeginSourceFile(m_clang_ap->getLangOpts(), &m_clang_ap->getPreprocessor()); + + if (m_code_generator_ptr) + delete m_code_generator_ptr; + + m_code_generator_ptr = CreateLLVMCodeGen(m_clang_ap->getDiagnostics(), + module_name, + m_clang_ap->getCodeGenOpts(), + m_clang_ap->getLLVMContext()); + + + // - CodeGeneration ASTConsumer (include/clang/ModuleBuilder.h), which will be passed in when you call... + // - Call clang::ParseAST (in lib/Sema/ParseAST.cpp) to parse the buffer. The CodeGenerator will generate code for __dbg_expr. + // - Once ParseAST completes, you can grab the llvm::Module from the CodeGenerator, which will have an llvm::Function you can hand off to the JIT. + ParseAST(m_clang_ap->getPreprocessor(), m_code_generator_ptr, m_clang_ap->getASTContext()); + + text_diagnostic_buffer.EndSourceFile(); + + //compiler_instance->getASTContext().getTranslationUnitDecl()->dump(); + + //if (compiler_instance->getFrontendOpts().ShowStats) { + // compiler_instance->getFileManager().PrintStats(); + // fprintf(stderr, "\n"); + //} + + // This code resolves the setClient above. + m_clang_ap->getDiagnostics().setClient(0); + + TextDiagnosticBuffer::const_iterator diag_iterator; + + int num_errors = 0; + +#ifdef COUNT_WARNINGS_AND_ERRORS + int num_warnings = 0; + + for (diag_iterator = text_diagnostic_buffer.warn_begin(); + diag_iterator != text_diagnostic_buffer.warn_end(); + ++diag_iterator) + num_warnings++; + + for (diag_iterator = text_diagnostic_buffer.err_begin(); + diag_iterator != text_diagnostic_buffer.err_end(); + ++diag_iterator) + num_errors++; + + if (num_warnings || num_errors) + { + if (num_warnings) + stream.Printf("%u warning%s%s", num_warnings, (num_warnings == 1 ? "" : "s"), (num_errors ? " and " : "")); + if (num_errors) + stream.Printf("%u error%s", num_errors, (num_errors == 1 ? "" : "s")); + stream.Printf("\n"); + } +#endif + + for (diag_iterator = text_diagnostic_buffer.warn_begin(); + diag_iterator != text_diagnostic_buffer.warn_end(); + ++diag_iterator) + stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); + + num_errors = 0; + + for (diag_iterator = text_diagnostic_buffer.err_begin(); + diag_iterator != text_diagnostic_buffer.err_end(); + ++diag_iterator) + { + num_errors++; + stream.Printf("error: %s\n", (*diag_iterator).second.c_str()); + } + + return num_errors; +} + + +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[0], + CI.getFrontendOpts().ASTMergeFiles.size()); + + return Act; +} + + +unsigned +ClangExpression::ConvertExpressionToDWARF (ClangExpressionVariableList& expr_local_variable_list, + StreamString &dwarf_opcode_strm) +{ + CompilerInstance *compiler_instance = GetCompilerInstance(); + + DeclarationName hack_func_name(&compiler_instance->getASTContext().Idents.get("___clang_expr")); + DeclContext::lookup_result result = compiler_instance->getASTContext().getTranslationUnitDecl()->lookup(hack_func_name); + + if (result.first != result.second) + { + Decl *decl = *result.first; + Stmt *decl_stmt = decl->getBody(); + if (decl_stmt) + { + ClangStmtVisitor visitor(compiler_instance->getASTContext(), expr_local_variable_list, m_decl_map, dwarf_opcode_strm); + + visitor.Visit (decl_stmt); + } + } + return 0; +} + +bool +ClangExpression::JITFunction (const ExecutionContext &exc_context, const char *name) +{ + + llvm::Module *module = m_code_generator_ptr->GetModule(); + + if (module) + { + std::string error; + + if (m_jit_mm_ptr == NULL) + m_jit_mm_ptr = new RecordingMemoryManager(); + + //llvm::InitializeNativeTarget(); + if (m_execution_engine.get() == 0) + m_execution_engine.reset(llvm::ExecutionEngine::createJIT (module, &error, m_jit_mm_ptr)); + m_execution_engine->DisableLazyCompilation(); + llvm::Function *function = module->getFunction (llvm::StringRef (name)); + + // We don't actually need the function pointer here, this just forces it to get resolved. + void *fun_ptr = m_execution_engine->getPointerToFunction(function); + // Note, you probably won't get here on error, since the LLVM JIT tends to just + // exit on error at present... So be careful. + if (fun_ptr == 0) + return false; + m_jitted_functions.push_back(ClangExpression::JittedFunction(name, (lldb::addr_t) fun_ptr)); + + } + return true; +} + +bool +ClangExpression::WriteJITCode (const ExecutionContext &exc_context) +{ + if (m_jit_mm_ptr == NULL) + return false; + + if (exc_context.process == NULL) + return false; + + // Look over the regions allocated for the function compiled. The JIT + // tries to allocate the functions & stubs close together, so we should try to + // write them that way too... + // For now I only write functions with no stubs, globals, exception tables, + // etc. So I only need to write the functions. + + size_t size = 0; + std::map::iterator fun_pos, fun_end = m_jit_mm_ptr->m_functions.end(); + for (fun_pos = m_jit_mm_ptr->m_functions.begin(); fun_pos != fun_end; fun_pos++) + { + size += (*fun_pos).second - (*fun_pos).first; + } + + Error error; + lldb::addr_t target_addr = exc_context.process->AllocateMemory (size, lldb::ePermissionsReadable|lldb::ePermissionsExecutable, error); + + if (target_addr == LLDB_INVALID_ADDRESS) + return false; + + lldb::addr_t cursor = target_addr; + for (fun_pos = m_jit_mm_ptr->m_functions.begin(); fun_pos != fun_end; fun_pos++) + { + lldb::addr_t lstart = (lldb::addr_t) (*fun_pos).first; + lldb::addr_t lend = (lldb::addr_t) (*fun_pos).second; + size_t size = lend - lstart; + exc_context.process->WriteMemory(cursor, (void *) lstart, size, error); + m_jit_mm_ptr->AddToLocalToRemoteMap (lstart, size, cursor); + cursor += size; + } + + std::vector::iterator pos, end = m_jitted_functions.end(); + + for (pos = m_jitted_functions.begin(); pos != end; pos++) + { + (*pos).m_remote_addr = m_jit_mm_ptr->GetRemoteAddressForLocal ((*pos).m_local_addr); + } + return true; +} + +lldb::addr_t +ClangExpression::GetFunctionAddress (const char *name) +{ + std::vector::iterator pos, end = m_jitted_functions.end(); + + for (pos = m_jitted_functions.begin(); pos < end; pos++) + { + if (strcmp ((*pos).m_name.c_str(), name) == 0) + return (*pos).m_remote_addr; + } + return LLDB_INVALID_ADDRESS; +} + +unsigned +ClangExpression::Compile() +{ + Mutex::Locker locker(GetClangMutex ()); + bool IsAST = false; + + if (CreateCompilerInstance(IsAST)) + { + // Validate/process some options + if (m_clang_ap->getHeaderSearchOpts().Verbose) + llvm::errs() << "clang-cc version " CLANG_VERSION_STRING + << " based upon " << PACKAGE_STRING + << " hosted on " << llvm::sys::getHostTriple() << "\n"; + + // Enforce certain implications. + if (!m_clang_ap->getFrontendOpts().ViewClassInheritance.empty()) + m_clang_ap->getFrontendOpts().ProgramAction = frontend::InheritanceView; +// if (!compiler_instance->getFrontendOpts().FixItSuffix.empty()) +// compiler_instance->getFrontendOpts().ProgramAction = frontend::FixIt; + + for (unsigned i = 0, e = m_clang_ap->getFrontendOpts().Inputs.size(); i != e; ++i) { + const std::string &InFile = m_clang_ap->getFrontendOpts().Inputs[i].second; + + // If we aren't using an AST file, setup the file and source managers and + // the preprocessor. + if (!IsAST) { + if (!i) { + // Create a file manager object to provide access to and cache the + // filesystem. + m_clang_ap->createFileManager(); + + // Create the source manager. + m_clang_ap->createSourceManager(); + } else { + // Reset the ID tables if we are reusing the SourceManager. + m_clang_ap->getSourceManager().clearIDTables(); + } + + // Create the preprocessor. + m_clang_ap->createPreprocessor(); + } + + llvm::OwningPtr Act(CreateFrontendAction(*m_clang_ap.get())); + if (!Act) + break; + + if (Act->BeginSourceFile(*m_clang_ap, InFile, IsAST)) { + Act->Execute(); + Act->EndSourceFile(); + } + } + + if (m_clang_ap->getDiagnosticOpts().ShowCarets) + { + unsigned NumWarnings = m_clang_ap->getDiagnostics().getNumWarnings(); + unsigned NumErrors = m_clang_ap->getDiagnostics().getNumErrors() - + m_clang_ap->getDiagnostics().getNumErrorsSuppressed(); + + if (NumWarnings || NumErrors) + { + if (NumWarnings) + fprintf (stderr, "%u warning%s%s", NumWarnings, (NumWarnings == 1 ? "" : "s"), (NumErrors ? " and " : "")); + if (NumErrors) + fprintf (stderr, "%u error%s", NumErrors, (NumErrors == 1 ? "" : "s")); + fprintf (stderr, " generated.\n"); + } + } + + if (m_clang_ap->getFrontendOpts().ShowStats) { + m_clang_ap->getFileManager().PrintStats(); + fprintf(stderr, "\n"); + } + + // Return the appropriate status when verifying diagnostics. + // + // FIXME: If we could make getNumErrors() do the right thing, we wouldn't need + // this. + if (m_clang_ap->getDiagnosticOpts().VerifyDiagnostics) + return static_cast(m_clang_ap->getDiagnosticClient()).HadErrors(); + + return m_clang_ap->getDiagnostics().getNumErrors(); + } + return 1; +} diff --git a/lldb/source/Expression/ClangExpressionDeclMap.cpp b/lldb/source/Expression/ClangExpressionDeclMap.cpp new file mode 100644 index 000000000000..1065211e45b6 --- /dev/null +++ b/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -0,0 +1,246 @@ +//===-- 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 "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/ExecutionContext.h" + +//#define DEBUG_CEDM +#ifdef DEBUG_CEDM +#define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +using namespace lldb_private; +using namespace clang; + +ClangExpressionDeclMap::ClangExpressionDeclMap(ExecutionContext *exe_ctx) : + m_exe_ctx(exe_ctx) +{ + if (exe_ctx && exe_ctx->frame) + m_sym_ctx = new SymbolContext(exe_ctx->frame->GetSymbolContext(lldb::eSymbolContextEverything)); + else + m_sym_ctx = NULL; +} + +ClangExpressionDeclMap::~ClangExpressionDeclMap() +{ + uint32_t num_tuples = m_tuples.size (); + uint32_t tuple_index; + + for (tuple_index = 0; tuple_index < num_tuples; ++tuple_index) + delete m_tuples[tuple_index].m_value; + + if (m_sym_ctx) + delete m_sym_ctx; +} + +bool +ClangExpressionDeclMap::GetIndexForDecl (uint32_t &index, + const clang::Decl *decl) +{ + uint32_t num_tuples = m_tuples.size (); + uint32_t tuple_index; + + for (tuple_index = 0; tuple_index < num_tuples; ++tuple_index) + { + if (m_tuples[tuple_index].m_decl == decl) + { + index = tuple_index; + return true; + } + } + + return false; +} + +// Interface for DwarfExpression +Value +*ClangExpressionDeclMap::GetValueForIndex (uint32_t index) +{ + if (index >= m_tuples.size ()) + return NULL; + + return m_tuples[index].m_value; +} + +// Interface for ClangASTSource +void +ClangExpressionDeclMap::GetDecls(NameSearchContext &context, + const char *name) +{ + DEBUG_PRINTF("Hunting for a definition for %s\n", name); + + // Back out in all cases where we're not fully initialized + if (!m_exe_ctx || !m_exe_ctx->frame || !m_sym_ctx) + return; + + Function *function = m_sym_ctx->function; + Block *block = m_sym_ctx->block; + + if (!function || !block) + { + DEBUG_PRINTF("function = %p, block = %p\n", function, block); + return; + } + + BlockList& blocks = function->GetBlocks(true); + + lldb::user_id_t current_block_id = block->GetID(); + + ConstString name_cs(name); + + for (current_block_id = block->GetID(); + current_block_id != Block::InvalidID; + current_block_id = blocks.GetParent(current_block_id)) + { + Block *current_block = blocks.GetBlockByID(current_block_id); + + lldb::VariableListSP var_list = current_block->GetVariableList(false, true); + + if (!var_list) + continue; + + lldb::VariableSP var = var_list->FindVariable(name_cs); + + if (!var) + continue; + + AddOneVariable(context, var.get()); + return; + } + + { + CompileUnit *compile_unit = m_sym_ctx->comp_unit; + + if (!compile_unit) + { + DEBUG_PRINTF("compile_unit = %p\n", compile_unit); + return; + } + + lldb::VariableListSP var_list = compile_unit->GetVariableList(true); + + if (!var_list) + return; + + lldb::VariableSP var = var_list->FindVariable(name_cs); + + if (!var) + return; + + AddOneVariable(context, var.get()); + return; + } +} + +void +ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context, + Variable* var) +{ + Type *var_type = var->GetType(); + + if (!var_type) + { + DEBUG_PRINTF("Skipped a definition for %s because it has no type\n", name); + return; + } + + void *var_opaque_type = var_type->GetOpaqueClangQualType(); + + if (!var_opaque_type) + { + DEBUG_PRINTF("Skipped a definition for %s because it has no Clang type\n", name); + return; + } + + DWARFExpression &var_location_expr = var->LocationExpression(); + + TypeList *type_list = var_type->GetTypeList(); + + if (!type_list) + { + DEBUG_PRINTF("Skipped a definition for %s because the type has no associated type list\n", name); + return; + } + + clang::ASTContext *exe_ast_ctx = type_list->GetClangASTContext().getASTContext(); + + if (!exe_ast_ctx) + { + DEBUG_PRINTF("There is no AST context for the current execution context\n"); + return; + } + + std::auto_ptr var_location(new Value); + + Error err; + + if (!var_location_expr.Evaluate(m_exe_ctx, exe_ast_ctx, NULL, *var_location.get(), &err)) + { + DEBUG_PRINTF("Error evaluating the location of %s: %s\n", name, err.AsCString()); + return; + } + + void *copied_type = ClangASTContext::CopyType(context.GetASTContext(), type_list->GetClangASTContext().getASTContext(), var_opaque_type); + + if (var_location.get()->GetContextType() == Value::eContextTypeInvalid) + var_location.get()->SetContext(Value::eContextTypeOpaqueClangQualType, copied_type); + + if (var_location.get()->GetValueType() == Value::eValueTypeFileAddress) + { + SymbolContext var_sc; + var->CalculateSymbolContext(&var_sc); + + if (!var_sc.module_sp) + return; + + ObjectFile *object_file = var_sc.module_sp->GetObjectFile(); + + if (!object_file) + return; + + Address so_addr(var_location->GetScalar().ULongLong(), object_file->GetSectionList()); + + lldb::addr_t load_addr = so_addr.GetLoadAddress(m_exe_ctx->process); + + var_location->GetScalar() = load_addr; + var_location->SetValueType(Value::eValueTypeLoadAddress); + } + + NamedDecl *var_decl = context.AddVarDecl(copied_type); + + Tuple tuple; + + tuple.m_decl = var_decl; + tuple.m_value = var_location.release(); + + m_tuples.push_back(tuple); + + DEBUG_PRINTF("Found for a definition for %s\n", name); +} diff --git a/lldb/source/Expression/ClangExpressionVariable.cpp b/lldb/source/Expression/ClangExpressionVariable.cpp new file mode 100644 index 000000000000..40fee4b47f56 --- /dev/null +++ b/lldb/source/Expression/ClangExpressionVariable.cpp @@ -0,0 +1,100 @@ +//===-- 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" + +using namespace lldb_private; +using namespace clang; + +ClangExpressionVariableList::ClangExpressionVariableList() : + m_variables() +{ +} + +ClangExpressionVariableList::~ClangExpressionVariableList() +{ + uint32_t num_variables = m_variables.size(); + uint32_t var_index; + + for (var_index = 0; var_index < num_variables; ++var_index) + delete m_variables[var_index].m_value; +} + +Value * +ValueForDecl(ASTContext &ast_context, const VarDecl *var_decl) +{ + Value *ret = new Value; + + ret->SetContext(Value::eContextTypeOpaqueClangQualType, + var_decl->getType().getAsOpaquePtr()); + + uint64_t bit_width = ast_context.getTypeSize(var_decl->getType()); + + uint32_t byte_size = (bit_width + 7 ) / 8; + + ret->ResizeData(byte_size); + + return ret; +} + +Value * +ClangExpressionVariableList::GetVariableForVarDecl (ASTContext &ast_context, const VarDecl *var_decl, uint32_t& idx, bool can_create) +{ + uint32_t num_variables = m_variables.size(); + uint32_t var_index; + + for (var_index = 0; var_index < num_variables; ++var_index) + { + if (m_variables[var_index].m_var_decl == var_decl) + { + idx = var_index; + return m_variables[var_index].m_value; + } + } + + if (!can_create) + return NULL; + + idx = m_variables.size(); + + ClangExpressionVariable val; + val.m_var_decl = var_decl; + val.m_value = ValueForDecl(ast_context, var_decl); + m_variables.push_back(val); + + return m_variables.back().m_value; +} + +Value * +ClangExpressionVariableList::GetVariableAtIndex (uint32_t idx) +{ + if (idx < m_variables.size()) + return m_variables[idx].m_value; + + return NULL; +} + +uint32_t +ClangExpressionVariableList::AppendValue (Value *value) +{ + uint32_t idx = m_variables.size(); + + ClangExpressionVariable val; + val.m_var_decl = NULL; + val.m_value = value; + + m_variables.push_back(val); + return idx; +} diff --git a/lldb/source/Expression/ClangFunction.cpp b/lldb/source/Expression/ClangFunction.cpp new file mode 100644 index 000000000000..e8713d0fd496 --- /dev/null +++ b/lldb/source/Expression/ClangFunction.cpp @@ -0,0 +1,671 @@ +//===-- 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/Frontend/CodeGenAction.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/Module.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "llvm/ADT/StringRef.h" + +// Project includes +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Core/DataExtractor.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/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(const char *target_triple, ClangASTContext *ast_context, void *return_qualtype, const Address& functionAddress, const ValueList &arg_value_list) : + ClangExpression (target_triple, NULL), + m_function_addr (functionAddress), + m_function_ptr (NULL), + m_arg_values (arg_value_list), + m_clang_ast_context (ast_context), + m_function_return_qual_type(return_qualtype), + m_wrapper_function_name ("__lldb_caller_function"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_return_offset(0), + m_compiled (false), + m_JITted (false) +{ +} + +ClangFunction::ClangFunction(const char *target_triple, Function &function, ClangASTContext *ast_context, const ValueList &arg_value_list) : + ClangExpression (target_triple, NULL), + m_function_ptr (&function), + m_arg_values (arg_value_list), + m_clang_ast_context (ast_context), + m_function_return_qual_type (NULL), + m_wrapper_function_name ("__lldb_function_caller"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_return_offset(0), + m_compiled (false), + m_JITted (false) +{ + m_function_addr = m_function_ptr->GetAddressRange().GetBaseAddress(); + m_function_return_qual_type = m_function_ptr->GetReturnType().GetOpaqueClangQualType(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangFunction::~ClangFunction() +{ +} + +unsigned +ClangFunction::CompileFunction (Stream &errors) +{ + // FIXME: How does clang tell us there's no return value? We need to handle that case. + unsigned num_errors = 0; + + if (!m_compiled) + { + std::string return_type_str = ClangASTContext::GetTypeName(m_function_return_qual_type); + + // 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... + std::string expression; + expression.append ("extern \"C\" void "); + expression.append (m_wrapper_function_name); + expression.append (" (void *input)\n{\n struct "); + expression.append (m_wrapper_struct_name); + expression.append (" \n {\n"); + expression.append (" "); + expression.append (return_type_str); + expression.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. + + size_t num_args = -1; + bool trust_function = false; + // GetArgumentCount returns -1 for an unprototyped function. + if (m_function_ptr) + { + num_args = m_function_ptr->GetArgumentCount(); + if (num_args != -1) + trust_function = true; + } + + if (num_args == -1) + 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 (int i = 0; i < num_args; i++) + { + const char *type_string; + std::string type_stdstr; + + if (trust_function) + { + type_string = m_function_ptr->GetArgumentTypeAtIndex(i).GetName().AsCString(); + } + else + { + Value *arg_value = m_arg_values.GetValueAtIndex(i); + void *clang_qual_type = arg_value->GetOpaqueClangQualType (); + if (clang_qual_type != NULL) + { + type_stdstr = ClangASTContext::GetTypeName(clang_qual_type); + type_string = type_stdstr.c_str(); + } + else + { + errors.Printf("Could not determine type of input value %d.", i); + return 1; + } + } + + + expression.append (type_string); + if (i < num_args - 1) + expression.append (", "); + + char arg_buf[32]; + args_buffer.append (" "); + args_buffer.append (type_string); + snprintf(arg_buf, 31, "arg_%d", 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 (", "); + + } + expression.append (");\n"); // Close off the function calling prototype. + + expression.append (args_buffer); + + expression.append (" "); + expression.append (return_type_str); + expression.append (" return_value;"); + expression.append ("\n };\n struct "); + expression.append (m_wrapper_struct_name); + expression.append ("* __lldb_fn_data = (struct "); + expression.append (m_wrapper_struct_name); + expression.append (" *) input;\n"); + + expression.append (" __lldb_fn_data->return_value = __lldb_fn_data->fn_ptr ("); + expression.append (args_list_buffer); + expression.append (");\n}\n"); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Expression: \n\n%s\n\n", expression.c_str()); + + // Okay, now compile this expression: + num_errors = ParseBareExpression (expression.c_str(), errors); + m_compiled = (num_errors == 0); + + if (m_compiled) + { + using namespace clang; + CompilerInstance *compiler_instance = GetCompilerInstance(); + ASTContext &ast_context = compiler_instance->getASTContext(); + + DeclarationName wrapper_func_name(&ast_context.Idents.get(m_wrapper_function_name.c_str())); + FunctionDecl::lookup_result func_lookup = ast_context.getTranslationUnitDecl()->lookup(wrapper_func_name); + if (func_lookup.first == func_lookup.second) + return false; + + FunctionDecl *wrapper_func = dyn_cast (*(func_lookup.first)); + if (!wrapper_func) + return false; + + DeclarationName wrapper_struct_name(&ast_context.Idents.get(m_wrapper_struct_name.c_str())); + RecordDecl::lookup_result struct_lookup = wrapper_func->lookup(wrapper_struct_name); + if (struct_lookup.first == struct_lookup.second) + return false; + + RecordDecl *wrapper_struct = dyn_cast(*(struct_lookup.first)); + + if (!wrapper_struct) + return false; + + m_struct_layout = &ast_context.getASTRecordLayout (wrapper_struct); + if (!m_struct_layout) + { + m_compiled = false; + return 1; + } + m_return_offset = m_struct_layout->getFieldOffset(m_struct_layout->getFieldCount() - 1); + m_return_size = (m_struct_layout->getDataSize() - m_return_offset)/8; + } + } + + return num_errors; +} + +bool +ClangFunction::WriteFunctionWrapper (ExecutionContext &exc_context, Stream &errors) +{ + Process *process = exc_context.process; + + if (process == NULL) + return false; + + if (!m_JITted) + { + // Next we should JIT it and insert the result into the target program. + if (!JITFunction (exc_context, m_wrapper_function_name.c_str())) + return false; + + if (!WriteJITCode (exc_context)) + return false; + + m_JITted = true; + } + + // Next get the call address for the function: + m_wrapper_fun_addr = GetFunctionAddress (m_wrapper_function_name.c_str()); + if (m_wrapper_fun_addr == LLDB_INVALID_ADDRESS) + return false; + + return true; +} + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exc_context, lldb::addr_t &args_addr_ref, Stream &errors) +{ + return WriteFunctionArguments(exc_context, 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 &exc_context, lldb::addr_t &args_addr_ref, Address function_address, ValueList &arg_values, Stream &errors) +{ + // Otherwise, allocate space for the argument passing struct, and write it. + // We use the information in the expression parser AST to + // figure out how to do this... + // We should probably transcode this in this object so we can ditch the compiler instance + // and all its associated data, and just keep the JITTed bytes. + + Error error; + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + Process *process = exc_context.process; + + if (process == NULL) + return return_value; + + uint64_t struct_size = m_struct_layout->getSize()/8; // Clang returns sizes in bytes. + + if (args_addr_ref == LLDB_INVALID_ADDRESS) + { + args_addr_ref = process->AllocateMemory(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; + } + } + + // FIXME: This is fake, and just assumes that it matches that architecture. + // Make a data extractor and put the address into the right byte order & size. + + uint64_t fun_addr = function_address.GetLoadAddress(exc_context.process); + int first_offset = m_struct_layout->getFieldOffset(0)/8; + process->WriteMemory(args_addr_ref + first_offset, &fun_addr, 8, 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: %d should be: %d", num_args, m_arg_values.GetSize()); + return false; + } + + for (int i = 0; i < num_args; i++) + { + // FIXME: We should sanity check sizes. + + int offset = m_struct_layout->getFieldOffset(i+1)/8; // 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::eContextTypeOpaqueClangQualType && + ClangASTContext::IsPointerType(arg_value->GetValueOpaqueClangQualType())) + continue; + + const Scalar &arg_scalar = arg_value->ResolveValue(&exc_context, m_clang_ast_context->getASTContext()); + + int byte_size = arg_scalar.GetByteSize(); + std::vector buffer; + buffer.resize(byte_size); + DataExtractor value_data; + arg_scalar.GetData (value_data); + value_data.ExtractBytes(0, byte_size, process->GetByteOrder(), buffer.data()); + process->WriteMemory(args_addr_ref + offset, buffer.data(), byte_size, error); + } + + return true; +} + +bool +ClangFunction::InsertFunction (ExecutionContext &exc_context, lldb::addr_t &args_addr_ref, Stream &errors) +{ + using namespace clang; + + if (CompileFunction(errors) != 0) + return false; + if (!WriteFunctionWrapper(exc_context, errors)) + return false; + if (!WriteFunctionArguments(exc_context, args_addr_ref, errors)) + return false; + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Call Address: 0x%llx Struct Address: 0x%llx.\n", m_wrapper_fun_addr, args_addr_ref); + + return true; +} + +ThreadPlan * +ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exc_context, lldb::addr_t &args_addr, Stream &errors, bool stop_others, bool discard_on_error) +{ + // FIXME: Use the errors Stream for better error reporting. + + Process *process = exc_context.process; + + if (process == NULL) + { + errors.Printf("Can't call a function without a process."); + return NULL; + } + + // Okay, now run the function: + + Address wrapper_address (NULL, m_wrapper_fun_addr); + ThreadPlan *new_plan = new ThreadPlanCallFunction (*exc_context.thread, + wrapper_address, + args_addr, + stop_others, discard_on_error); + return new_plan; +} + +bool +ClangFunction::FetchFunctionResults (ExecutionContext &exc_context, 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. + + std::vector data_buffer; + data_buffer.resize(m_return_size); + Process *process = exc_context.process; + Error error; + size_t bytes_read = process->ReadMemory(args_addr + m_return_offset/8, data_buffer.data(), m_return_size, error); + + if (bytes_read == 0) + { + return false; + } + + if (bytes_read < m_return_size) + return false; + + DataExtractor data(data_buffer.data(), m_return_size, process->GetByteOrder(), process->GetAddressByteSize()); + // FIXME: Assuming an integer scalar for now: + + uint32_t offset = 0; + uint64_t return_integer = data.GetMaxU64(&offset, m_return_size); + + ret_value.SetContext (Value::eContextTypeOpaqueClangQualType, m_function_return_qual_type); + ret_value.SetValueType(Value::eValueTypeScalar); + ret_value.GetScalar() = return_integer; + return true; +} + +void +ClangFunction::DeallocateFunctionResults (ExecutionContext &exc_context, 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); + + exc_context.process->DeallocateMemory(args_addr); +} + +ClangFunction::ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exc_context, Stream &errors, Value &results) +{ + return ExecuteFunction (exc_context, errors, 1000, true, results); +} + +ClangFunction::ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exc_context, Stream &errors, bool stop_others, Value &results) +{ + return ExecuteFunction (exc_context, NULL, errors, stop_others, NULL, false, results); +} + +ClangFunction::ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exc_context, + Stream &errors, + uint32_t single_thread_timeout_usec, + bool try_all_threads, + Value &results) +{ + return ExecuteFunction (exc_context, NULL, errors, true, single_thread_timeout_usec, try_all_threads, results); +} + +ClangFunction::ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exc_context, + lldb::addr_t *args_addr_ptr, + Stream &errors, + bool stop_others, + uint32_t single_thread_timeout_usec, + bool try_all_threads, + Value &results) +{ + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + Process *process = exc_context.process; + + 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(exc_context, args_addr, errors)) + return eExecutionSetupError; + } + + + lldb::ThreadPlanSP call_plan_sp(GetThreadPlanToCallFunction(exc_context, args_addr, errors, stop_others, false)); + + ThreadPlanCallFunction *call_plan_ptr = static_cast (call_plan_sp.get()); + + if (args_addr_ptr != NULL) + *args_addr_ptr = args_addr; + + if (call_plan_sp == NULL) + return return_value; + + call_plan_sp->SetPrivate(true); + exc_context.thread->QueueThreadPlan(call_plan_sp, true); + + // We need to call the function synchronously, so spin waiting for it to return. + // If we get interrupted while executing, we're going to lose our context, and + // won't be able to gather the result at this point. + + TimeValue* timeout_ptr = NULL; + TimeValue real_timeout; + if (single_thread_timeout_usec != 0) + { + real_timeout = TimeValue::Now(); + real_timeout.OffsetWithMicroSeconds(single_thread_timeout_usec); + timeout_ptr = &real_timeout; + } + process->Resume (); + + + while (1) + { + lldb::EventSP event_sp; + + // Now wait for the process to stop again: + // FIXME: Probably want a time out. + lldb::StateType stop_state = process->WaitForStateChangedEvents (timeout_ptr, event_sp); + if (stop_state == lldb::eStateInvalid && timeout_ptr != NULL) + { + // Right now this is the only way to tell we've timed out... + // We should interrupt the process here... + // Not really sure what to do if Halt fails here... + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Running function with timeout: %d timed out, trying with all threads enabled.", single_thread_timeout_usec); + + if (process->Halt().Success()) + { + timeout_ptr = NULL; + + lldb::StateType stop_state = process->WaitForStateChangedEvents (timeout_ptr, event_sp); + if (stop_state == lldb::eStateInvalid) + { + errors.Printf ("Got an invalid stop state after halt."); + } + else if (stop_state != lldb::eStateStopped) + { + StreamString s; + event_sp->Dump (&s); + + errors.Printf("Didn't get a stopped event after Halting the target, got: \"%s\"", s.GetData()); + } + + if (try_all_threads) + { + // Between the time that we got the timeout and the time we halted, but target + // might have actually completed the plan. If so, we're done. + if (exc_context.thread->IsThreadPlanDone (call_plan_sp.get())) + { + return_value = eExecutionCompleted; + break; + } + + + call_plan_ptr->SetStopOthers (false); + process->Resume(); + continue; + } + else + return eExecutionInterrupted; + } + } + if (stop_state == lldb::eStateRunning || stop_state == lldb::eStateStepping) + continue; + + if (exc_context.thread->IsThreadPlanDone (call_plan_sp.get())) + { + return_value = eExecutionCompleted; + break; + } + else if (exc_context.thread->WasThreadPlanDiscarded (call_plan_sp.get())) + { + return_value = eExecutionDiscarded; + break; + } + else + { + return_value = eExecutionInterrupted; + break; + } + + } + + if (return_value != eExecutionCompleted) + return return_value; + + FetchFunctionResults(exc_context, args_addr, results); + + if (args_addr_ptr == NULL) + DeallocateFunctionResults(exc_context, args_addr); + + return eExecutionCompleted; +} + +ClangFunction::ExecutionResults +ClangFunction::ExecuteFunctionWithABI(ExecutionContext &exc_context, Stream &errors, Value &results) +{ + // FIXME: Use the errors Stream for better error reporting. + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + Process *process = exc_context.process; + + if (process == NULL) + { + errors.Printf("Can't call a function without a process."); + return return_value; + } + + //unsigned int num_args = m_arg_values.GetSize(); + //unsigned int arg_index; + + //for (arg_index = 0; arg_index < num_args; ++arg_index) + // m_arg_values.GetValueAtIndex(arg_index)->ResolveValue(&exc_context, GetASTContext()); + + ThreadPlan *call_plan = exc_context.thread->QueueThreadPlanForCallFunction (false, + m_function_addr, + m_arg_values, + true); + if (call_plan == NULL) + return return_value; + + call_plan->SetPrivate(true); + + // We need to call the function synchronously, so spin waiting for it to return. + // If we get interrupted while executing, we're going to lose our context, and + // won't be able to gather the result at this point. + + process->Resume (); + + while (1) + { + lldb::EventSP event_sp; + + // Now wait for the process to stop again: + // FIXME: Probably want a time out. + lldb::StateType stop_state = process->WaitForStateChangedEvents (NULL, event_sp); + if (stop_state == lldb::eStateRunning || stop_state == lldb::eStateStepping) + continue; + + if (exc_context.thread->IsThreadPlanDone (call_plan)) + { + return_value = eExecutionCompleted; + break; + } + else if (exc_context.thread->WasThreadPlanDiscarded (call_plan)) + { + return_value = eExecutionDiscarded; + break; + } + else + { + return_value = eExecutionInterrupted; + break; + } + + } + + return eExecutionCompleted; +} diff --git a/lldb/source/Expression/ClangStmtVisitor.cpp b/lldb/source/Expression/ClangStmtVisitor.cpp new file mode 100644 index 000000000000..1d2f53fcb80e --- /dev/null +++ b/lldb/source/Expression/ClangStmtVisitor.cpp @@ -0,0 +1,1032 @@ +//===-- ClangStmtVisitor.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/ClangStmtVisitor.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "clang/AST/RecordLayout.h" + +#define NO_RTTI +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionVariable.h" + +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#include +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +// Project includes + +static lldb_private::Scalar::Type +GetScalarTypeForClangType (clang::ASTContext &ast_context, clang::QualType clang_type, uint32_t &count) +{ + count = 1; + + switch (clang_type->getTypeClass()) + { + case clang::Type::FunctionNoProto: + case clang::Type::FunctionProto: + break; + + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + break; + + case clang::Type::ConstantArray: + break; + + case clang::Type::ExtVector: + case clang::Type::Vector: + // TODO: Set this to more than one??? + break; + + case clang::Type::Builtin: + switch (cast(clang_type)->getKind()) + { + default: assert(0 && "Unknown builtin type!"); + case clang::BuiltinType::Void: + break; + + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::WChar: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: + return lldb_private::Scalar::GetValueTypeForSignedIntegerWithByteSize ((ast_context.getTypeSize(clang_type)/CHAR_BIT)/count); + + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::NullPtr: + return lldb_private::Scalar::GetValueTypeForUnsignedIntegerWithByteSize ((ast_context.getTypeSize(clang_type)/CHAR_BIT)/count); + + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: + return lldb_private::Scalar::GetValueTypeForFloatWithByteSize ((ast_context.getTypeSize(clang_type)/CHAR_BIT)/count); + } + break; + // All pointer types are represented as unsigned integer encodings. + // We may nee to add a eEncodingPointer if we ever need to know the + // difference + case clang::Type::ObjCObjectPointer: + case clang::Type::BlockPointer: + case clang::Type::Pointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: + return lldb_private::Scalar::GetValueTypeForUnsignedIntegerWithByteSize ((ast_context.getTypeSize(clang_type)/CHAR_BIT)/count); + + // Complex numbers are made up of floats + case clang::Type::Complex: + count = 2; + return lldb_private::Scalar::GetValueTypeForFloatWithByteSize ((ast_context.getTypeSize(clang_type)/CHAR_BIT) / count); + + case clang::Type::ObjCInterface: break; + case clang::Type::Record: break; + case clang::Type::Enum: + return lldb_private::Scalar::GetValueTypeForSignedIntegerWithByteSize ((ast_context.getTypeSize(clang_type)/CHAR_BIT)/count); + + case clang::Type::Typedef: + return GetScalarTypeForClangType(ast_context, cast(clang_type)->LookThroughTypedefs(), count); + break; + + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + //case clang::Type::QualifiedName: + case clang::Type::TemplateSpecialization: break; + } + count = 0; + return lldb_private::Scalar::e_void; +} + +//---------------------------------------------------------------------- +// ClangStmtVisitor constructor +//---------------------------------------------------------------------- +lldb_private::ClangStmtVisitor::ClangStmtVisitor +( + clang::ASTContext &ast_context, + lldb_private::ClangExpressionVariableList &variable_list, + lldb_private::ClangExpressionDeclMap *decl_map, + lldb_private::StreamString &strm +) : + m_ast_context (ast_context), + m_variable_list (variable_list), + m_decl_map (decl_map), + m_stream (strm) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +lldb_private::ClangStmtVisitor::~ClangStmtVisitor() +{ +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitStmt (clang::Stmt *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + + clang::Stmt::child_iterator pos; + clang::Stmt::child_iterator begin = Node->child_begin(); + clang::Stmt::child_iterator end = Node->child_end(); + bool clear_before_next_stmt = false; + for (pos = begin; pos != end; ++pos) + { +#ifdef ENABLE_DEBUG_PRINTF + pos->dump(); +#endif + clang::Stmt *child_stmt = *pos; + uint32_t pre_visit_stream_offset = m_stream.GetSize(); + bool not_null_stmt = dyn_cast(child_stmt) == NULL; + if (clear_before_next_stmt && not_null_stmt) + m_stream.PutHex8(DW_OP_APPLE_clear); + Visit (child_stmt); + if (not_null_stmt) + clear_before_next_stmt = pre_visit_stream_offset != m_stream.GetSize(); + } +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitDeclStmt (clang::DeclStmt *decl_stmt) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + clang::DeclGroupRef decl_group_ref = decl_stmt->getDeclGroup(); + clang::DeclGroupRef::iterator pos, end = decl_group_ref.end(); + for (pos = decl_group_ref.begin(); pos != end; ++pos) + { + clang::Decl *decl = *pos; + if (decl) + { + clang::Decl::Kind decl_kind = decl->getKind(); + + switch (decl_kind) + { + case clang::Decl::Namespace: + case clang::Decl::Enum: + case clang::Decl::Record: + case clang::Decl::CXXRecord: + case clang::Decl::ObjCMethod: + case clang::Decl::ObjCInterface: + case clang::Decl::ObjCCategory: + case clang::Decl::ObjCProtocol: + case clang::Decl::ObjCImplementation: + case clang::Decl::ObjCCategoryImpl: + case clang::Decl::LinkageSpec: + case clang::Decl::Block: + case clang::Decl::Function: + case clang::Decl::CXXMethod: + case clang::Decl::CXXConstructor: + case clang::Decl::CXXDestructor: + case clang::Decl::CXXConversion: + case clang::Decl::Field: + case clang::Decl::Typedef: + case clang::Decl::EnumConstant: + case clang::Decl::ImplicitParam: + case clang::Decl::ParmVar: + case clang::Decl::ObjCProperty: + break; + + case clang::Decl::Var: + { + const clang::VarDecl *var_decl = cast(decl)->getCanonicalDecl(); + uint32_t expr_local_var_idx = UINT32_MAX; + if (m_variable_list.GetVariableForVarDecl (m_ast_context, var_decl, expr_local_var_idx, true)) + { + const clang::Expr* var_decl_expr = var_decl->getAnyInitializer(); + // If there is an inialization expression, then assign the + // variable. + if (var_decl_expr) + { + m_stream.PutHex8(DW_OP_APPLE_expr_local); + m_stream.PutULEB128(expr_local_var_idx); + Visit ((clang::Stmt *)var_decl_expr); + m_stream.PutHex8(DW_OP_APPLE_assign); + } + } + } + break; + + default: + assert(!"decl unhandled"); + break; + } + } + } +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitLabelStmt (clang::LabelStmt *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitGotoStmt (clang::GotoStmt *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +// Exprs +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitExpr (clang::Expr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitDeclRefExpr (clang::DeclRefExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + clang::NamedDecl *decl = Node->getDecl(); + clang::QualType clang_type = Node->getType(); + +#ifdef ENABLE_DEBUG_PRINTF + //decl->dump(); + //clang_type.dump("lldb_private::ClangStmtVisitor::VisitDeclRefExpr() -> clang_type.dump() = "); +#endif + uint32_t expr_local_var_idx = UINT32_MAX; + if (m_variable_list.GetVariableForVarDecl (m_ast_context, cast(decl)->getCanonicalDecl(), expr_local_var_idx, false) && + expr_local_var_idx != UINT32_MAX) + { + m_stream.PutHex8(DW_OP_APPLE_expr_local); + m_stream.PutULEB128(expr_local_var_idx); + } + else if (m_decl_map && + m_decl_map->GetIndexForDecl(expr_local_var_idx, decl->getCanonicalDecl())) + { + m_stream.PutHex8(DW_OP_APPLE_extern); + m_stream.PutULEB128(expr_local_var_idx); + } + else + { + m_stream.PutHex8 (DW_OP_APPLE_error); + } +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitPredefinedExpr (clang::PredefinedExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitCharacterLiteral (clang::CharacterLiteral *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + clang::QualType clang_type = Node->getType(); + uint64_t clang_type_size = m_ast_context.getTypeSize (clang_type); + if (clang_type_size <= 64) + { + // Encode the integer into our DWARF expression + if (clang_type->isSignedIntegerType()) + EncodeSInt64(Node->getValue(), clang_type_size); + else + EncodeUInt64(Node->getValue(), clang_type_size); + } + else + { + // TODO: eventually support integer math over 64 bits, probably using + // APInt as the class. + m_stream.PutHex8(DW_OP_APPLE_error); + } +} + +bool +lldb_private::ClangStmtVisitor::EncodeUInt64 (uint64_t uval, uint32_t bit_size) +{ + // If "bit_size" is zero, then encode "uval" in the most efficient way + if (bit_size <= 8 || (bit_size == 0 && uval <= UINT8_MAX)) + { + m_stream.PutHex8 (DW_OP_const1u); + m_stream.PutHex8 (uval); + } + else if (bit_size <= 16 || (bit_size == 0 && uval <= UINT16_MAX)) + { + m_stream.PutHex8 (DW_OP_const2u); + m_stream.PutHex16 (uval); + } + else if (bit_size <= 32 || (bit_size == 0 && uval <= UINT32_MAX)) + { + m_stream.PutHex8 (DW_OP_const4u); + m_stream.PutHex32 (uval); + } + else if (bit_size <= 64 || (bit_size == 0)) + { + m_stream.PutHex8 (DW_OP_const8u); + m_stream.PutHex64 (uval); + } + else + { + m_stream.PutHex8 (DW_OP_APPLE_error); + return false; + } + return true; +} + +bool +lldb_private::ClangStmtVisitor::EncodeSInt64 (int64_t sval, uint32_t bit_size) +{ + if (bit_size <= 8 || (bit_size == 0 && INT8_MIN <= sval && sval <= INT8_MAX)) + { + m_stream.PutHex8 (DW_OP_const1s); + m_stream.PutHex8 (sval); + } + else if (bit_size <= 16 || (bit_size == 0 && INT16_MIN <= sval && sval <= INT16_MAX)) + { + m_stream.PutHex8 (DW_OP_const2s); + m_stream.PutHex16 (sval); + } + else if (bit_size <= 32 || (bit_size == 0 && INT32_MIN <= sval && sval <= INT32_MAX)) + { + m_stream.PutHex8 (DW_OP_const4s); + m_stream.PutHex32 (sval); + } + else if (bit_size <= 64 || (bit_size == 0)) + { + m_stream.PutHex8 (DW_OP_const8s); + m_stream.PutHex64 (sval); + } + else + { + m_stream.PutHex8 (DW_OP_APPLE_error); + return false; + } + return true; +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitIntegerLiteral (clang::IntegerLiteral *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + const llvm::APInt &ap_int = Node->getValue(); + if (ap_int.getBitWidth() <= 64) + { + clang::QualType clang_type = Node->getType(); + uint64_t clang_type_size = m_ast_context.getTypeSize (clang_type); + // Encode the integer into our DWARF expression + if (clang_type->isSignedIntegerType()) + EncodeSInt64(ap_int.getLimitedValue(), clang_type_size); + else + EncodeUInt64(ap_int.getLimitedValue(), clang_type_size); + } + else + { + // TODO: eventually support integer math over 64 bits, probably using + // APInt as the class. + m_stream.PutHex8(DW_OP_APPLE_error); + } +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitFloatingLiteral (clang::FloatingLiteral *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + const llvm::APFloat &ap_float = Node->getValue(); + // Put the length of the float in bytes into a single byte + llvm::APInt ap_int(ap_float.bitcastToAPInt()); + const unsigned byte_size = ap_int.getBitWidth() / CHAR_BIT; + if (byte_size == sizeof(float)) + { + if (sizeof(float) == 4) + { + m_stream.PutHex8(DW_OP_APPLE_constf); + m_stream.PutHex8 (byte_size); + m_stream.PutHex32 (ap_int.getLimitedValue()); + return; + } + else if (sizeof(float) == 8) + { + m_stream.PutHex8(DW_OP_APPLE_constf); + m_stream.PutHex8 (byte_size); + m_stream.PutHex64 (ap_int.getLimitedValue()); + return; + } + } + else if (byte_size == sizeof(double)) + { + if (sizeof(double) == 4) + { + m_stream.PutHex8(DW_OP_APPLE_constf); + m_stream.PutHex8 (byte_size); + m_stream.PutHex32 (ap_int.getLimitedValue()); + return; + } + else if (sizeof(double) == 8) + { + m_stream.PutHex8(DW_OP_APPLE_constf); + m_stream.PutHex8 (byte_size); + m_stream.PutHex64 (ap_int.getLimitedValue()); + return; + } + } + else if (byte_size == sizeof(long double)) + { + if (sizeof(long double) == 8) + { + m_stream.PutHex8(DW_OP_APPLE_constf); + m_stream.PutHex8 (byte_size); + m_stream.PutHex64 (ap_int.getLimitedValue()); + return; + } + } + // TODO: eventually support float constants of all sizes using + // APFloat as the class. + m_stream.PutHex8(DW_OP_APPLE_error); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitStringLiteral (clang::StringLiteral *Str) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + + size_t byte_length = Str->getByteLength(); + bool is_wide = Str->isWide(); + + size_t new_length = byte_length + (is_wide ? 1 : 2); + + uint8_t null_terminated_string[new_length]; + + memcpy(&null_terminated_string[0], Str->getStrData(), byte_length); + + if(is_wide) + { + null_terminated_string[byte_length] = '\0'; + null_terminated_string[byte_length + 1] = '\0'; + } + else + { + null_terminated_string[byte_length] = '\0'; + } + + Value *val = new Value(null_terminated_string, new_length); + val->SetContext(Value::eContextTypeOpaqueClangQualType, Str->getType().getAsOpaquePtr()); + + uint32_t val_idx = m_variable_list.AppendValue(val); + + m_stream.PutHex8(DW_OP_APPLE_expr_local); + m_stream.PutULEB128(val_idx); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitUnaryOperator (clang::UnaryOperator *unary_op) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + + Visit(unary_op->getSubExpr()); + + switch (unary_op->getOpcode()) + { + case clang::UnaryOperator::PostInc: + // Duplciate the top of stack value (which must be something that can + // be assignable/incremented) and push its current value + m_stream.PutHex8 (DW_OP_dup); // x, x + m_stream.PutHex8 (DW_OP_APPLE_value_of); // x, val(x) + m_stream.PutHex8 (DW_OP_swap); // val(x), x + m_stream.PutHex8 (DW_OP_dup); // val(x), x, x + m_stream.PutHex8 (DW_OP_lit1); // val(x), x, x, 1 + m_stream.PutHex8 (DW_OP_plus); // val(x), x, val(x)+1 + m_stream.PutHex8 (DW_OP_APPLE_assign); // val(x), x + m_stream.PutHex8 (DW_OP_drop); // val(x) + break; + + case clang::UnaryOperator::PostDec: + // Duplciate the top of stack value (which must be something that can + // be assignable/incremented) and push its current value + m_stream.PutHex8 (DW_OP_dup); // x, x + m_stream.PutHex8 (DW_OP_APPLE_value_of); // x, val(x) + m_stream.PutHex8 (DW_OP_swap); // val(x), x + m_stream.PutHex8 (DW_OP_dup); // val(x), x, x + m_stream.PutHex8 (DW_OP_lit1); // val(x), x, x, 1 + m_stream.PutHex8 (DW_OP_minus); // val(x), x, val(x)-1 + m_stream.PutHex8 (DW_OP_APPLE_assign); // val(x), x + m_stream.PutHex8 (DW_OP_drop); // val(x) + break; + + case clang::UnaryOperator::PreInc: + m_stream.PutHex8 (DW_OP_dup); // x, x + m_stream.PutHex8 (DW_OP_APPLE_value_of); // x, val(x) + m_stream.PutHex8 (DW_OP_lit1); // x, val(x), 1 + m_stream.PutHex8 (DW_OP_plus); // x, val(x)+1 + m_stream.PutHex8 (DW_OP_APPLE_assign); // x with new value + break; + + case clang::UnaryOperator::PreDec: + m_stream.PutHex8 (DW_OP_dup); // x, x + m_stream.PutHex8 (DW_OP_APPLE_value_of); // x, val(x) + m_stream.PutHex8 (DW_OP_lit1); // x, val(x), 1 + m_stream.PutHex8 (DW_OP_minus); // x, val(x)-1 + m_stream.PutHex8 (DW_OP_APPLE_assign); // x with new value + break; + + case clang::UnaryOperator::AddrOf: + m_stream.PutHex8 (DW_OP_APPLE_address_of); + break; + + case clang::UnaryOperator::Deref: + m_stream.PutHex8 (DW_OP_APPLE_deref_type); + break; + + case clang::UnaryOperator::Plus: + m_stream.PutHex8 (DW_OP_abs); + break; + + case clang::UnaryOperator::Minus: + m_stream.PutHex8 (DW_OP_neg); + break; + + case clang::UnaryOperator::Not: + m_stream.PutHex8 (DW_OP_not); + break; + + case clang::UnaryOperator::LNot: + m_stream.PutHex8 (DW_OP_lit0); + m_stream.PutHex8 (DW_OP_eq); + break; + + case clang::UnaryOperator::Real: + m_stream.PutHex8(DW_OP_APPLE_error); + break; + + case clang::UnaryOperator::Imag: + m_stream.PutHex8(DW_OP_APPLE_error); + break; + + case clang::UnaryOperator::Extension: + m_stream.PutHex8(DW_OP_APPLE_error); + break; + + case clang::UnaryOperator::OffsetOf: + break; + + default: + assert(!"Unknown unary operator!"); + break; + } +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitCastExpr (clang::CastExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +// CastExpr::CastKind cast_kind = Node->getCastKind(); +// switch (cast_kind) +// { +// case CastExpr::CK_Unknown: +// case CastExpr::CK_BitCast: // Used for reinterpret_cast. +// case CastExpr::CK_NoOp: // Used for const_cast. +// case CastExpr::CK_BaseToDerived: // Base to derived class casts. +// case CastExpr::CK_DerivedToBase: // Derived to base class casts. +// case CastExpr::CK_Dynamic: // Dynamic cast. +// case CastExpr::CK_ToUnion: // Cast to union (GCC extension). +// case CastExpr::CK_ArrayToPointerDecay: // Array to pointer decay. +// case CastExpr::CK_FunctionToPointerDecay: // Function to pointer decay. +// case CastExpr::CK_NullToMemberPointer: // Null pointer to member pointer. +// case CastExpr::CK_BaseToDerivedMemberPointer: // Member pointer in base class to member pointer in derived class. +// case CastExpr::CK_DerivedToBaseMemberPointer: // Member pointer in derived class to member pointer in base class. +// case CastExpr::CK_UserDefinedConversion: // Conversion using a user defined type conversion function. +// case CastExpr::CK_ConstructorConversion: // Conversion by constructor +// case CastExpr::CK_IntegralToPointer: // Integral to pointer +// case CastExpr::CK_PointerToIntegral: // Pointer to integral +// case CastExpr::CK_ToVoid: // Cast to void +// case CastExpr::CK_VectorSplat: // Casting from an integer/floating type to an extended +// // vector type with the same element type as the src type. Splats the +// // src expression into the destination expression. +// case CastExpr::CK_IntegralCast: // Casting between integral types of different size. +// case CastExpr::CK_IntegralToFloating: // Integral to floating point. +// case CastExpr::CK_FloatingToIntegral: // Floating point to integral. +// case CastExpr::CK_FloatingCast: // Casting between floating types of different size. +// m_stream.PutHex8(DW_OP_APPLE_error); +// break; +// } + uint32_t cast_type_count = 0; + lldb_private::Scalar::Type cast_type_encoding = GetScalarTypeForClangType (m_ast_context, Node->getType(), cast_type_count); + + + Visit (Node->getSubExpr()); + + // Simple scalar cast + if (cast_type_encoding != lldb_private::Scalar::e_void && cast_type_count == 1) + { + // Only cast if our scalar types mismatch + uint32_t castee_type_count = 0; + lldb_private::Scalar::Type castee_type_encoding = GetScalarTypeForClangType (m_ast_context, Node->getSubExpr()->getType(), castee_type_count); + if (cast_type_encoding != castee_type_encoding && + castee_type_encoding != lldb_private::Scalar::e_void) + { + m_stream.PutHex8(DW_OP_APPLE_scalar_cast); + m_stream.PutHex8(cast_type_encoding); + } + } + else + { + // Handle more complex casts with clang types soon! + m_stream.PutHex8(DW_OP_APPLE_error); + } +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitArraySubscriptExpr (clang::ArraySubscriptExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + Visit (Node->getBase()); + Visit (Node->getIdx()); + m_stream.PutHex8(DW_OP_APPLE_array_ref); +} + +// +//CLANG_STMT_RESULT +//lldb_private::ClangStmtVisitor::VisitImplicitCastExpr (clang::ImplicitCastExpr *Node) +//{ +// DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +// m_stream.PutHex8(DW_OP_APPLE_scalar_cast); +// Visit (Node->getSubExpr()); +//} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitSizeOfAlignOfExpr (clang::SizeOfAlignOfExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitMemberExpr (clang::MemberExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + clang::Expr *parent = Node->getBase(); + Visit (parent); + clang::QualType parent_clang_type = parent->getType(); + clang::NamedDecl *member_named_decl = cast(Node->getMemberDecl()->getCanonicalDecl()); + +// DeclarationName member_name = member->getDeclName(); + + clang::Type::TypeClass parent_type_class = parent_clang_type->getTypeClass(); + if (parent_type_class == clang::Type::Pointer) + { + clang::PointerType *pointer_type = cast(parent_clang_type.getTypePtr()); + parent_clang_type = pointer_type->getPointeeType(); + parent_type_class = parent_clang_type->getTypeClass(); + } + + switch (parent_type_class) + { + case clang::Type::Record: + { + const clang::RecordType *record_type = cast(parent_clang_type.getTypePtr()); + const clang::RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const clang::ASTRecordLayout &record_layout = m_ast_context.getASTRecordLayout(record_decl); + uint32_t field_idx = 0; + clang::RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx) + { + clang::NamedDecl *field_named_decl = cast(field->getCanonicalDecl()); + if (field_named_decl == member_named_decl) + { + std::pair field_type_info = m_ast_context.getTypeInfo(field->getType()); + uint64_t field_bit_offset = record_layout.getFieldOffset (field_idx); + uint64_t field_byte_offset = field_bit_offset / 8; + uint32_t field_bitfield_bit_size = 0; + //uint32_t field_bitfield_bit_offset = field_bit_offset % 8; + + if (field->isBitField()) + { + clang::Expr* bit_width_expr = field->getBitWidth(); + if (bit_width_expr) + { + llvm::APSInt bit_width_apsint; + if (bit_width_expr->isIntegerConstantExpr(bit_width_apsint, m_ast_context)) + { + field_bitfield_bit_size = bit_width_apsint.getLimitedValue(UINT32_MAX); + } + } + } + + if (Node->isArrow()) + { + m_stream.PutHex8(DW_OP_deref); + } + else + { + m_stream.PutHex8(DW_OP_APPLE_address_of); + } + + if (field_byte_offset) + { + if (EncodeUInt64(field_byte_offset, 0)) + { + m_stream.PutHex8(DW_OP_plus); + } + } + m_stream.PutHex8(DW_OP_APPLE_clang_cast); + m_stream.PutPointer(field->getType().getAsOpaquePtr()); + break; + } + } + } + break; + + default: + assert(!"Unhandled MemberExpr"); + break; + } +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitExtVectorElementExpr (clang::ExtVectorElementExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitParenExpr(clang::ParenExpr *paren_expr) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + Visit (paren_expr->getSubExpr()); +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitInitListExpr (clang::InitListExpr *init_list_expr) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitBinaryOperator (clang::BinaryOperator *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); + + Visit(Node->getLHS()); + Visit(Node->getRHS()); + + switch (Node->getOpcode()) + { + default: assert(0 && "Unknown binary operator!"); + case clang::BinaryOperator::PtrMemD: m_stream.PutHex8(DW_OP_APPLE_error); break; + case clang::BinaryOperator::PtrMemI: m_stream.PutHex8(DW_OP_APPLE_error); break; + case clang::BinaryOperator::Mul: m_stream.PutHex8(DW_OP_mul); break; + case clang::BinaryOperator::Div: m_stream.PutHex8(DW_OP_div); break; + case clang::BinaryOperator::Rem: m_stream.PutHex8(DW_OP_mod); break; + case clang::BinaryOperator::Add: m_stream.PutHex8(DW_OP_plus); break; + case clang::BinaryOperator::Sub: m_stream.PutHex8(DW_OP_minus); break; + case clang::BinaryOperator::Shl: m_stream.PutHex8(DW_OP_shl); break; + case clang::BinaryOperator::Shr: m_stream.PutHex8(DW_OP_shr); break; + case clang::BinaryOperator::LT: m_stream.PutHex8(DW_OP_lt); break; + case clang::BinaryOperator::GT: m_stream.PutHex8(DW_OP_gt); break; + case clang::BinaryOperator::LE: m_stream.PutHex8(DW_OP_le); break; + case clang::BinaryOperator::GE: m_stream.PutHex8(DW_OP_ge); break; + case clang::BinaryOperator::EQ: m_stream.PutHex8(DW_OP_eq); break; + case clang::BinaryOperator::NE: m_stream.PutHex8(DW_OP_ne); break; + case clang::BinaryOperator::And: m_stream.PutHex8(DW_OP_and); break; + case clang::BinaryOperator::Xor: m_stream.PutHex8(DW_OP_xor); break; + case clang::BinaryOperator::Or : m_stream.PutHex8(DW_OP_or); break; + case clang::BinaryOperator::LAnd: + // Do we need to call an operator here on objects? If so + // we will need a DW_OP_apple_logical_and + m_stream.PutHex8(DW_OP_lit0); + m_stream.PutHex8(DW_OP_ne); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_lit0); + m_stream.PutHex8(DW_OP_ne); + m_stream.PutHex8(DW_OP_and); + break; + + case clang::BinaryOperator::LOr : + // Do we need to call an operator here on objects? If so + // we will need a DW_OP_apple_logical_or + m_stream.PutHex8(DW_OP_lit0); + m_stream.PutHex8(DW_OP_ne); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_lit0); + m_stream.PutHex8(DW_OP_ne); + m_stream.PutHex8(DW_OP_or); + break; + + case clang::BinaryOperator::Assign: + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::MulAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_mul); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::DivAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_div); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::RemAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_mod); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::AddAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_plus); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::SubAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_minus); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::ShlAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_shl); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::ShrAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_shr); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::AndAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_and); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::OrAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_or); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::XorAssign: + m_stream.PutHex8(DW_OP_over); + m_stream.PutHex8(DW_OP_swap); + m_stream.PutHex8(DW_OP_xor); + m_stream.PutHex8(DW_OP_APPLE_assign); + break; + + case clang::BinaryOperator::Comma: + // Nothing needs to be done here right? + break; + } +} + + +//CLANG_STMT_RESULT +//lldb_private::ClangStmtVisitor::VisitCompoundAssignOperator (CompoundAssignOperator *Node) +//{ +// DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +// +//} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitAddrLabelExpr (clang::AddrLabelExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitTypesCompatibleExpr (clang::TypesCompatibleExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + + + // C++ +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitCXXNamedCastExpr (clang::CXXNamedCastExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitCXXBoolLiteralExpr (clang::CXXBoolLiteralExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitCXXThisExpr (clang::CXXThisExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitCXXFunctionalCastExpr (clang::CXXFunctionalCastExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + + + // ObjC +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCEncodeExpr (clang::ObjCEncodeExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCMessageExpr (clang::ObjCMessageExpr* Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCSelectorExpr (clang::ObjCSelectorExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCProtocolExpr (clang::ObjCProtocolExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCPropertyRefExpr (clang::ObjCPropertyRefExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCImplicitSetterGetterRefExpr (clang::ObjCImplicitSetterGetterRefExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCIvarRefExpr (clang::ObjCIvarRefExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + +CLANG_STMT_RESULT +lldb_private::ClangStmtVisitor::VisitObjCSuperExpr (clang::ObjCSuperExpr *Node) +{ + DEBUG_PRINTF("%s\n", __PRETTY_FUNCTION__); +} + + diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp new file mode 100644 index 000000000000..d7afa36cea71 --- /dev/null +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -0,0 +1,2589 @@ +//===-- 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/dwarf.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" + +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionVariable.h" + +#include "lldb/Host/Host.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.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_base_addr(), + m_expr_locals (NULL), + m_decl_map (NULL) +{ +} + +DWARFExpression::DWARFExpression(const DWARFExpression& rhs) : + m_data(rhs.m_data), + m_reg_kind (rhs.m_reg_kind), + m_loclist_base_addr(rhs.m_loclist_base_addr), + m_expr_locals (rhs.m_expr_locals), + m_decl_map (rhs.m_decl_map) +{ +} + + +DWARFExpression::DWARFExpression(const DataExtractor& data, uint32_t data_offset, uint32_t data_length, const Address* loclist_base_addr_ptr) : + m_data(data, data_offset, data_length), + m_reg_kind (eRegisterKindDWARF), + m_loclist_base_addr(), + m_expr_locals (NULL), + m_decl_map (NULL) +{ + if (loclist_base_addr_ptr) + m_loclist_base_addr = *loclist_base_addr_ptr; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DWARFExpression::~DWARFExpression() +{ +} + + +bool +DWARFExpression::IsValid() const +{ + return m_data.GetByteSize() > 0; +} + + +void +DWARFExpression::SetExpressionLocalVariableList (ClangExpressionVariableList *locals) +{ + m_expr_locals = locals; +} + +void +DWARFExpression::SetExpressionDeclMap (ClangExpressionDeclMap *decl_map) +{ + m_decl_map = decl_map; +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data, const Address* loclist_base_addr_ptr) +{ + m_data = data; + if (loclist_base_addr_ptr != NULL) + m_loclist_base_addr = *loclist_base_addr_ptr; + else + m_loclist_base_addr.Clear(); +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data, uint32_t data_offset, uint32_t data_length, const Address* loclist_base_addr_ptr) +{ + m_data.SetData(data, data_offset, data_length); + if (loclist_base_addr_ptr != NULL) + m_loclist_base_addr = *loclist_base_addr_ptr; + else + m_loclist_base_addr.Clear(); +} + +void +DWARFExpression::DumpLocation (Stream *s, uint32_t offset, uint32_t length, lldb::DescriptionLevel level) const +{ + if (!m_data.ValidOffsetForDataOfSize(offset, length)) + return; + const uint32_t start_offset = offset; + const uint32_t end_offset = offset + length; + while (m_data.ValidOffset(offset) && offset < end_offset) + { + const uint32_t op_offset = offset; + const uint8_t op = m_data.GetU8(&offset); + + switch (level) + { + 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.8x: %s", op_offset, op >= DW_OP_APPLE_uninit ? "DW_OP_APPLE_" : "DW_OP_"); + break; + } + + switch (op) + { + case DW_OP_addr: *s << "addr(" << m_data.GetAddress(&offset) << ") "; break; // 0x03 1 address + case DW_OP_deref: *s << "deref"; break; // 0x06 + case DW_OP_const1u: s->Printf("const1u(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x08 1 1-byte constant + case DW_OP_const1s: s->Printf("const1s(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x09 1 1-byte constant + case DW_OP_const2u: s->Printf("const2u(0x%4.4x) ", m_data.GetU16(&offset)); break; // 0x0a 1 2-byte constant + case DW_OP_const2s: s->Printf("const2s(0x%4.4x) ", m_data.GetU16(&offset)); break; // 0x0b 1 2-byte constant + case DW_OP_const4u: s->Printf("const4u(0x%8.8x) ", m_data.GetU32(&offset)); break; // 0x0c 1 4-byte constant + case DW_OP_const4s: s->Printf("const4s(0x%8.8x) ", m_data.GetU32(&offset)); break; // 0x0d 1 4-byte constant + case DW_OP_const8u: s->Printf("const8u(0x%16.16llx) ", m_data.GetU64(&offset)); break; // 0x0e 1 8-byte constant + case DW_OP_const8s: s->Printf("const8s(0x%16.16llx) ", m_data.GetU64(&offset)); break; // 0x0f 1 8-byte constant + case DW_OP_constu: s->Printf("constu(0x%x) ", m_data.GetULEB128(&offset)); break; // 0x10 1 ULEB128 constant + case DW_OP_consts: s->Printf("consts(0x%x) ", m_data.GetSLEB128(&offset)); break; // 0x11 1 SLEB128 constant + case DW_OP_dup: s->PutCString("dup"); break; // 0x12 + case DW_OP_drop: s->PutCString("drop"); break; // 0x13 + case DW_OP_over: s->PutCString("over"); break; // 0x14 + case DW_OP_pick: s->Printf("pick(0x%2.2x) ", m_data.GetU8(&offset)); break; // 0x15 1 1-byte stack index + case DW_OP_swap: s->PutCString("swap"); break; // 0x16 + case DW_OP_rot: s->PutCString("rot"); break; // 0x17 + case DW_OP_xderef: s->PutCString("xderef"); break; // 0x18 + case DW_OP_abs: s->PutCString("abs"); break; // 0x19 + case DW_OP_and: s->PutCString("and"); break; // 0x1a + case DW_OP_div: s->PutCString("div"); break; // 0x1b + case DW_OP_minus: s->PutCString("minus"); break; // 0x1c + case DW_OP_mod: s->PutCString("mod"); break; // 0x1d + case DW_OP_mul: s->PutCString("mul"); break; // 0x1e + case DW_OP_neg: s->PutCString("neg"); break; // 0x1f + case DW_OP_not: s->PutCString("not"); break; // 0x20 + case DW_OP_or: s->PutCString("or"); break; // 0x21 + case DW_OP_plus: s->PutCString("plus"); break; // 0x22 + case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend + s->Printf("plus_uconst(0x%x) ", m_data.GetULEB128(&offset)); + break; + + case DW_OP_shl: s->PutCString("shl"); break; // 0x24 + case DW_OP_shr: s->PutCString("shr"); break; // 0x25 + case DW_OP_shra: s->PutCString("shra"); break; // 0x26 + case DW_OP_xor: s->PutCString("xor"); break; // 0x27 + case DW_OP_skip: s->Printf("skip(0x%4.4x)", m_data.GetU16(&offset)); break; // 0x2f 1 signed 2-byte constant + case DW_OP_bra: s->Printf("bra(0x%4.4x)", m_data.GetU16(&offset)); break; // 0x28 1 signed 2-byte constant + case DW_OP_eq: s->PutCString("eq"); break; // 0x29 + case DW_OP_ge: s->PutCString("ge"); break; // 0x2a + case DW_OP_gt: s->PutCString("gt"); break; // 0x2b + case DW_OP_le: s->PutCString("le"); break; // 0x2c + case DW_OP_lt: s->PutCString("lt"); break; // 0x2d + case DW_OP_ne: s->PutCString("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("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: s->Printf("reg%i", op - DW_OP_reg0); break; // 0x6f + + 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: s->Printf("breg%i(0x%x)", op - DW_OP_breg0, m_data.GetULEB128(&offset)); break; + + case DW_OP_regx: // 0x90 1 ULEB128 register + s->Printf("regx(0x%x)", m_data.GetULEB128(&offset)); + break; + case DW_OP_fbreg: // 0x91 1 SLEB128 offset + s->Printf("fbreg(0x%x)",m_data.GetSLEB128(&offset)); + break; + case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset + s->Printf("bregx(0x%x, 0x%x)", m_data.GetULEB128(&offset), m_data.GetSLEB128(&offset)); + break; + case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed + s->Printf("piece(0x%x)", m_data.GetULEB128(&offset)); + break; + case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved + s->Printf("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("xderef_size(0x%2.2x)", m_data.GetU8(&offset)); + break; + case DW_OP_nop: s->PutCString("nop"); break; // 0x96 + case DW_OP_push_object_address: s->PutCString("push_object_address"); break; // 0x97 DWARF3 + case DW_OP_call2: // 0x98 DWARF3 1 2-byte offset of DIE + s->Printf("call2(0x%4.4x)", m_data.GetU16(&offset)); + break; + case DW_OP_call4: // 0x99 DWARF3 1 4-byte offset of DIE + s->Printf("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("call_ref(0x%8.8llx)", 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("bit_piece(0x%x, 0x%x)", m_data.GetULEB128(&offset), m_data.GetULEB128(&offset)); +// break; +// case DW_OP_lo_user: s->PutCString("lo_user"); break; // 0xe0 +// case DW_OP_hi_user: s->PutCString("hi_user"); break; // 0xff + case DW_OP_APPLE_extern: + s->Printf("extern(%u)", m_data.GetULEB128(&offset)); + break; + case DW_OP_APPLE_array_ref: + s->PutCString("array_ref"); + break; + case DW_OP_APPLE_uninit: + s->PutCString("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("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("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("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("deref_type"); + break; + case DW_OP_APPLE_expr_local: // 0xF5 - ULEB128 expression local index + s->Printf("expr_local(%u)", 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("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("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("clang_cast(%p)", clang_type); + } + break; + case DW_OP_APPLE_clear: + s->PutCString("clear"); + break; + case DW_OP_APPLE_error: // 0xFF - Stops expression evaluation and returns an error (no args) + s->PutCString("error"); + break; + } + } +} + +void +DWARFExpression::SetLocationListBaseAddress(Address& base_addr) +{ + m_loclist_base_addr = base_addr; +} + +int +DWARFExpression::GetRegisterKind () +{ + return m_reg_kind; +} + +void +DWARFExpression::SetRegisterKind (int reg_kind) +{ + m_reg_kind = reg_kind; +} + +bool +DWARFExpression::IsLocationList() const +{ + return m_loclist_base_addr.IsSectionOffset(); +} + +void +DWARFExpression::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + if (IsLocationList()) + { + // We have a location list + uint32_t offset = 0; + uint32_t count = 0; + Address base_addr(m_loclist_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(", "); + AddressRange addr_range(base_addr, end_addr_offset - begin_addr_offset); + addr_range.GetBaseAddress().SetOffset(base_addr.GetOffset() + begin_addr_offset); + addr_range.Dump (s, NULL, Address::DumpStyleFileAddress); + s->PutChar('{'); + uint32_t location_length = m_data.GetU16(&offset); + DumpLocation (s, offset, location_length, level); + 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 == 0xFFFFFFFFull || + m_data.GetAddressByteSize() == 8 && begin_addr_offset == 0xFFFFFFFFFFFFFFFFull) + { + // 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); + } +} + +static bool +ReadRegisterValueAsScalar +( + ExecutionContext *exe_ctx, + uint32_t reg_kind, + uint32_t reg_num, + Error *error_ptr, + Value &value +) +{ + if (exe_ctx && exe_ctx->frame) + { + RegisterContext *reg_context = exe_ctx->frame->GetRegisterContext(); + + if (reg_context == NULL) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); + } + else + { + uint32_t native_reg = reg_context->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 + { + value.SetValueType (Value::eValueTypeScalar); + value.SetContext (Value::eContextTypeDCRegisterInfo, const_cast(reg_context->GetRegisterInfoAtIndex(native_reg))); + + if (reg_context->ReadRegisterValue (native_reg, value.GetScalar())) + return true; + + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Failed to read register %u.\n", native_reg); + } + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid frame in execution context.\n"); + } + return false; +} + +bool +DWARFExpression::LocationListContainsLoadAddress (Process* process, const Address &addr) const +{ + if (IsLocationList()) + { + uint32_t offset = 0; + const addr_t load_addr = addr.GetLoadAddress(process); + + if (load_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t loc_list_base_addr = m_loclist_base_addr.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; +} +bool +DWARFExpression::Evaluate +( + ExecutionContextScope *exe_scope, + clang::ASTContext *ast_context, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + ExecutionContext exe_ctx (exe_scope); + return Evaluate(&exe_ctx, ast_context, initial_value_ptr, result, error_ptr); +} + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + if (IsLocationList()) + { + uint32_t offset = 0; + addr_t pc = exe_ctx->frame->GetPC().GetLoadAddress(exe_ctx->process); + + if (pc == LLDB_INVALID_ADDRESS) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid PC in frame."); + return false; + } + + addr_t loc_list_base_addr = m_loclist_base_addr.GetLoadAddress(exe_ctx->process); + + if (loc_list_base_addr == LLDB_INVALID_ADDRESS) + { + if (error_ptr) + error_ptr->SetErrorString("Out of scope."); + 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; + + uint16_t length = m_data.GetU16(&offset); + + if (length > 0 && lo_pc <= pc && pc < hi_pc) + { + return DWARFExpression::Evaluate (exe_ctx, ast_context, m_data, m_expr_locals, m_decl_map, offset, length, m_reg_kind, initial_value_ptr, result, error_ptr); + } + offset += length; + } + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Out of scope.\n", pc); + return false; + } + + // Not a location list, just a single expression. + return DWARFExpression::Evaluate (exe_ctx, ast_context, m_data, m_expr_locals, m_decl_map, 0, m_data.GetByteSize(), m_reg_kind, initial_value_ptr, result, error_ptr); +} + + + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + const DataExtractor& opcodes, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + const uint32_t opcodes_offset, + const uint32_t opcodes_length, + const uint32_t reg_kind, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) +{ + std::vector stack; + + if (initial_value_ptr) + stack.push_back(*initial_value_ptr); + + uint32_t offset = opcodes_offset; + const uint32_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 uint32_t op_offset = offset; + const uint8_t op = opcodes.GetU8(&offset); + + if (log) + { + log->Printf("\n"); + size_t count = stack.size(); + for (size_t i=0; iPrintf("%s", new_value.GetData()); + } + log->Printf("0x%8.8x: %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(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 (exe_ctx->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 = exe_ctx->process->GetAddressByteSize(); + Error error; + if (exe_ctx->process->ReadMemory(pointer_addr, &addr_bytes, addr_size, error) == addr_size) + { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), exe_ctx->process->GetByteOrder(), addr_size); + uint32_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%llx for DW_OP_deref: %s\n", + pointer_addr, + error.AsCString()); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL process for DW_OP_deref.\n"); + return false; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("NULL execution context for DW_OP_deref.\n"); + return false; + } + break; + + default: + break; + } + + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_deref_size + // OPERANDS: 1 + // 1 - uint8_t that specifies the size of the data to dereference. + // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top + // stack entry and treats it as an address. The value retrieved from that + // address is pushed. In the DW_OP_deref_size operation, however, the + // size in bytes of the data retrieved from the dereferenced address is + // specified by the single operand. This operand is a 1-byte unsigned + // integral constant whose value may not be larger than the size of an + // address on the target machine. The data retrieved is zero extended + // to the size of an address on the target machine before being pushed + // on the expression stack. + //---------------------------------------------------------------------- + case DW_OP_deref_size: + if (error_ptr) + error_ptr->SetErrorString("Unimplemented opcode: DW_OP_deref_size."); + return false; + + //---------------------------------------------------------------------- + // 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(( uint8_t)opcodes.GetU8(&offset)); break; + case DW_OP_const1s : stack.push_back(( int8_t)opcodes.GetU8(&offset)); break; + case DW_OP_const2u : stack.push_back((uint16_t)opcodes.GetU16(&offset)); break; + case DW_OP_const2s : stack.push_back(( int16_t)opcodes.GetU16(&offset)); break; + case DW_OP_const4u : stack.push_back((uint32_t)opcodes.GetU32(&offset)); break; + case DW_OP_const4s : stack.push_back(( int32_t)opcodes.GetU32(&offset)); break; + case DW_OP_const8u : stack.push_back((uint64_t)opcodes.GetU64(&offset)); break; + case DW_OP_const8s : stack.push_back(( int64_t)opcodes.GetU64(&offset)); break; + case DW_OP_constu : stack.push_back(opcodes.GetULEB128(&offset)); break; + case DW_OP_consts : stack.push_back(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, ast_context).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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) & tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context).IsZero()) + { + if (error_ptr) + error_ptr->SetErrorString("Divide by zero."); + return false; + } + else + { + stack.pop_back(); + stack.back() = stack.back().ResolveValue(exe_ctx, ast_context) / tmp.ResolveValue(exe_ctx, ast_context); + if (!stack.back().ResolveValue(exe_ctx, ast_context).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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) - tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) % tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) * tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context).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, ast_context).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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) | tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) + tmp.ResolveValue(exe_ctx, ast_context); + } + 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 + { + uint32_t uconst_value = opcodes.GetULEB128(&offset); + // Implicit conversion from a UINT to a Scalar... + stack.back().ResolveValue(exe_ctx, ast_context) += uconst_value; + if (!stack.back().ResolveValue(exe_ctx, ast_context).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, ast_context) <<= tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context).ShiftRightLogical(tmp.ResolveValue(exe_ctx, ast_context)) == 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, ast_context) >>= tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) ^ tmp.ResolveValue(exe_ctx, ast_context); + } + 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); + uint32_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, ast_context) != zero) + { + uint32_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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) == tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) >= tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) > tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) <= tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) < tmp.ResolveValue(exe_ctx, ast_context); + } + 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, ast_context) = stack.back().ResolveValue(exe_ctx, ast_context) != tmp.ResolveValue(exe_ctx, ast_context); + } + 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(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 (exe_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 (exe_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 (exe_ctx, reg_kind, reg_num, error_ptr, tmp)) + { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx, ast_context) += (uint64_t)breg_offset; + 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 (exe_ctx, reg_kind, reg_num, error_ptr, tmp)) + { + int64_t breg_offset = opcodes.GetSLEB128(&offset); + tmp.ResolveValue(exe_ctx, ast_context) += (uint64_t)breg_offset; + stack.push_back(tmp); + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + else + return false; + } + break; + + case DW_OP_fbreg: + if (exe_ctx && exe_ctx->frame) + { + Scalar value; + if (exe_ctx->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; + } + 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_call_ref + // OPERANDS: + // uint32_t absolute DIE offset for 32-bit DWARF or a uint64_t + // absolute DIE offset for 64 bit DWARF. + // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF + // expression. Takes a single operand. In the 32-bit DWARF format, the + // operand is a 4-byte unsigned value; in the 64-bit DWARF format, it + // is an 8-byte unsigned value. The operand is used as the offset of a + // debugging information entry in a .debug_info section which may be + // contained in a shared object for executable other than that + // containing the operator. For references from one shared object or + // executable to another, the relocation must be performed by the + // consumer. + // + // Operand interpretation of DW_OP_call_ref is exactly like that for + // DW_FORM_ref_addr. + // + // 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_call_ref: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_call_ref."); + return false; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_array_ref + // OPERANDS: none + // DESCRIPTION: Pops a value off the stack and uses it as the array + // index. Pops a second value off the stack and uses it as the array + // itself. Pushes a value onto the stack representing the element of + // the array specified by the index. + //---------------------------------------------------------------------- + case DW_OP_APPLE_array_ref: + { + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_APPLE_array_ref."); + return false; + } + + Value index_val = stack.back(); + stack.pop_back(); + Value array_val = stack.back(); + stack.pop_back(); + + Scalar &index_scalar = index_val.ResolveValue(exe_ctx, ast_context); + int64_t index = index_scalar.SLongLong(LONG_LONG_MAX); + + if (index == LONG_LONG_MAX) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid array index."); + return false; + } + + if (array_val.GetContextType() != Value::eContextTypeOpaqueClangQualType) + { + if (error_ptr) + error_ptr->SetErrorString("Arrays without Clang types are unhandled at this time."); + return false; + } + + if (array_val.GetValueType() != Value::eValueTypeLoadAddress && + array_val.GetValueType() != Value::eValueTypeHostAddress) + { + if (error_ptr) + error_ptr->SetErrorString("Array must be stored in memory."); + return false; + } + + void *array_type = array_val.GetOpaqueClangQualType(); + + void *member_type; + uint64_t size = 0; + + if ((!ClangASTContext::IsPointerType(array_type, &member_type)) && + (!ClangASTContext::IsArrayType(array_type, &member_type, &size))) + { + if (error_ptr) + error_ptr->SetErrorString("Array reference from something that is neither a pointer nor an array."); + return false; + } + + if (size && (index >= size || index < 0)) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Out of bounds array access. %lld is not in [0, %llu]", index, size); + return false; + } + + uint64_t member_bit_size = ClangASTContext::GetTypeBitSize(ast_context, member_type); + uint64_t member_bit_align = ClangASTContext::GetTypeBitAlign(ast_context, member_type); + uint64_t member_bit_incr = ((member_bit_size + member_bit_align - 1) / member_bit_align) * member_bit_align; + if (member_bit_incr % 8) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Array increment is not byte aligned", index, size); + return false; + } + int64_t member_offset = (int64_t)(member_bit_incr / 8) * index; + + Value member; + + member.SetContext(Value::eContextTypeOpaqueClangQualType, member_type); + member.SetValueType(array_val.GetValueType()); + + addr_t array_base = (addr_t)array_val.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + addr_t member_loc = array_base + member_offset; + member.GetScalar() = (uint64_t)member_loc; + + stack.push_back(member); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_uninit + // OPERANDS: none + // DESCRIPTION: Lets us know that the value is currently not initialized + //---------------------------------------------------------------------- + case DW_OP_APPLE_uninit: + //return eResultTypeErrorUninitialized; + break; // Ignore this as we have seen cases where this value is incorrectly added + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_assign + // OPERANDS: none + // DESCRIPTION: Pops a value off of the stack and assigns it to the next + // item on the stack which must be something assignable (inferior + // Variable, inferior Type with address, inferior register, or + // expression local variable. + //---------------------------------------------------------------------- + case DW_OP_APPLE_assign: + if (stack.size() < 2) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 2 items for DW_OP_APPLE_assign."); + return false; + } + else + { + tmp = stack.back(); + stack.pop_back(); + Value::ContextType context_type = stack.back().GetContextType(); + StreamString new_value(Stream::eBinary, 4, eByteOrderHost); + switch (context_type) + { + case Value::eContextTypeOpaqueClangQualType: + { + void *clang_type = stack.back().GetOpaqueClangQualType(); + + if (ClangASTContext::IsAggregateType (clang_type)) + { + Value::ValueType source_value_type = tmp.GetValueType(); + Value::ValueType target_value_type = stack.back().GetValueType(); + + addr_t source_addr = (addr_t)tmp.GetScalar().ULongLong(); + addr_t target_addr = (addr_t)stack.back().GetScalar().ULongLong(); + + size_t byte_size = (ClangASTContext::GetTypeBitSize(ast_context, clang_type) + 7) / 8; + + switch (source_value_type) + { + case Value::eValueTypeLoadAddress: + switch (target_value_type) + { + case Value::eValueTypeLoadAddress: + { + DataBufferHeap data; + data.SetByteSize(byte_size); + + Error error; + if (exe_ctx->process->ReadMemory (source_addr, data.GetBytes(), byte_size, error) != byte_size) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Couldn't read a composite type from the target: %s", error.AsCString()); + return false; + } + + if (exe_ctx->process->WriteMemory (target_addr, data.GetBytes(), byte_size, error) != byte_size) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Couldn't write a composite type to the target: %s", error.AsCString()); + return false; + } + } + break; + case Value::eValueTypeHostAddress: + if (exe_ctx->process->GetByteOrder() != Host::GetByteOrder()) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Copy of composite types between incompatible byte orders is unimplemented"); + return false; + } + else + { + Error error; + if (exe_ctx->process->ReadMemory (source_addr, (uint8_t*)target_addr, byte_size, error) != byte_size) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Couldn't read a composite type from the target: %s", error.AsCString()); + return false; + } + } + break; + default: + return false; + } + break; + case Value::eValueTypeHostAddress: + switch (target_value_type) + { + case Value::eValueTypeLoadAddress: + if (exe_ctx->process->GetByteOrder() != Host::GetByteOrder()) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Copy of composite types between incompatible byte orders is unimplemented"); + return false; + } + else + { + Error error; + if (exe_ctx->process->WriteMemory (target_addr, (uint8_t*)source_addr, byte_size, error) != byte_size) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Couldn't write a composite type to the target: %s", error.AsCString()); + return false; + } + } + case Value::eValueTypeHostAddress: + memcpy ((uint8_t*)target_addr, (uint8_t*)source_addr, byte_size); + break; + default: + return false; + } + } + } + else + { + if (!Type::SetValueFromScalar(ast_context, + clang_type, + tmp.ResolveValue(exe_ctx, ast_context), + new_value)) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Couldn't extract a value from an integral type.\n"); + return false; + } + + Value::ValueType value_type = stack.back().GetValueType(); + + switch (value_type) + { + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + { + lldb::AddressType address_type = (value_type == Value::eValueTypeLoadAddress ? eAddressTypeLoad : eAddressTypeHost); + lldb::addr_t addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (!Type::WriteToMemory (exe_ctx, + ast_context, + clang_type, + addr, + address_type, + new_value)) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Failed to write value to memory at 0x%llx.\n", addr); + return false; + } + } + break; + + default: + break; + } + } + } + break; + + default: + if (error_ptr) + error_ptr->SetErrorString ("Assign failed."); + return false; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_address_of + // OPERANDS: none + // DESCRIPTION: Pops a value off of the stack and pushed its address. + // The top item on the stack must be a variable, or already be a memory + // location. + //---------------------------------------------------------------------- + case DW_OP_APPLE_address_of: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_APPLE_address_of."); + return false; + } + else + { + Value::ValueType value_type = stack.back().GetValueType(); + switch (value_type) + { + default: + case Value::eValueTypeScalar: // raw scalar value + if (error_ptr) + error_ptr->SetErrorString("Top stack item isn't a memory based object."); + return false; + + case Value::eValueTypeLoadAddress: // load address value + case Value::eValueTypeFileAddress: // file address value + case Value::eValueTypeHostAddress: // host address value (for memory in the process that is using liblldb) + // Taking the address of an object reduces it to the address + // of the value and removes any extra context it had. + //stack.back().SetValueType(Value::eValueTypeScalar); + stack.back().ClearContext(); + break; + } + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_value_of + // OPERANDS: none + // DESCRIPTION: Pops a value off of the stack and pushed its value. + // The top item on the stack must be a variable, expression variable. + //---------------------------------------------------------------------- + case DW_OP_APPLE_value_of: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 items for DW_OP_APPLE_value_of."); + return false; + } + else if (!stack.back().ValueOf(exe_ctx, ast_context)) + { + if (error_ptr) + error_ptr->SetErrorString ("Top stack item isn't a valid candidate for DW_OP_APPLE_value_of."); + return false; + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_deref_type + // OPERANDS: none + // DESCRIPTION: gets the value pointed to by the top stack item + //---------------------------------------------------------------------- + case DW_OP_APPLE_deref_type: + { + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 items for DW_OP_APPLE_deref_type."); + return false; + } + + tmp = stack.back(); + stack.pop_back(); + + if (tmp.GetContextType() != Value::eContextTypeOpaqueClangQualType) + { + if (error_ptr) + error_ptr->SetErrorString("Item at top of expression stack must have a Clang type"); + return false; + } + + void *ptr_type = tmp.GetOpaqueClangQualType(); + void *target_type; + + if (!ClangASTContext::IsPointerType(ptr_type, &target_type)) + { + if (error_ptr) + error_ptr->SetErrorString("Dereferencing a non-pointer type"); + return false; + } + + // TODO do we want all pointers to be dereferenced as load addresses? + Value::ValueType value_type = tmp.GetValueType(); + + tmp.ResolveValue(exe_ctx, ast_context); + + tmp.SetValueType(value_type); + tmp.SetContext(Value::eContextTypeOpaqueClangQualType, target_type); + + stack.push_back(tmp); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_expr_local + // OPERANDS: ULEB128 + // DESCRIPTION: pushes the expression local variable index onto the + // stack and set the appropriate context so we know the stack item is + // an expression local variable index. + //---------------------------------------------------------------------- + case DW_OP_APPLE_expr_local: + { + uint32_t idx = opcodes.GetULEB128(&offset); + if (expr_locals == NULL) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("DW_OP_APPLE_expr_local(%u) opcode encountered with no local variable list.\n", idx); + return false; + } + Value *expr_local_variable = expr_locals->GetVariableAtIndex(idx); + if (expr_local_variable == NULL) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("DW_OP_APPLE_expr_local(%u) with invalid index %u.\n", idx, idx); + return false; + } + Value *proxy = expr_local_variable->CreateProxy(); + stack.push_back(*proxy); + delete proxy; + //stack.back().SetContext (Value::eContextTypeOpaqueClangQualType, expr_local_variable->GetOpaqueClangQualType()); + } + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_extern + // OPERANDS: ULEB128 + // DESCRIPTION: pushes a proxy for the extern object index onto the + // stack. + //---------------------------------------------------------------------- + case DW_OP_APPLE_extern: + { + uint32_t idx = opcodes.GetULEB128(&offset); + if (!decl_map) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("DW_OP_APPLE_extern(%u) opcode encountered with no decl map.\n", idx); + return false; + } + Value *extern_var = decl_map->GetValueForIndex(idx); + if (!extern_var) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("DW_OP_APPLE_extern(%u) with invalid index %u.\n", idx, idx); + return false; + } + Value *proxy = extern_var->CreateProxy(); + stack.push_back(*proxy); + delete proxy; + } + break; + + case DW_OP_APPLE_scalar_cast: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_APPLE_scalar_cast."); + return false; + } + else + { + // Simple scalar cast + if (!stack.back().ResolveValue(exe_ctx, ast_context).Cast((Scalar::Type)opcodes.GetU8(&offset))) + { + if (error_ptr) + error_ptr->SetErrorString("Cast failed."); + return false; + } + } + break; + + + case DW_OP_APPLE_clang_cast: + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString("Expression stack needs at least 1 item for DW_OP_APPLE_clang_cast."); + return false; + } + else + { + void *clang_type = (void *)opcodes.GetMaxU64(&offset, sizeof(void*)); + stack.back().SetContext (Value::eContextTypeOpaqueClangQualType, clang_type); + } + break; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_constf + // OPERANDS: 1 byte float length, followed by that many bytes containing + // the constant float data. + // DESCRIPTION: Push a float value onto the expression stack. + //---------------------------------------------------------------------- + case DW_OP_APPLE_constf: // 0xF6 - 1 byte float size, followed by constant float data + { + uint8_t float_length = opcodes.GetU8(&offset); + if (sizeof(float) == float_length) + tmp.ResolveValue(exe_ctx, ast_context) = opcodes.GetFloat (&offset); + else if (sizeof(double) == float_length) + tmp.ResolveValue(exe_ctx, ast_context) = opcodes.GetDouble (&offset); + else if (sizeof(long double) == float_length) + tmp.ResolveValue(exe_ctx, ast_context) = opcodes.GetLongDouble (&offset); + else + { + StreamString new_value; + opcodes.Dump(&new_value, offset, eFormatBytes, 1, float_length, UINT32_MAX, DW_INVALID_ADDRESS, 0, 0); + + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("DW_OP_APPLE_constf(<%u> %s) unsupported float size.\n", float_length, new_value.GetData()); + return false; + } + tmp.SetValueType(Value::eValueTypeScalar); + tmp.ClearContext(); + stack.push_back(tmp); + } + break; + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_clear + // OPERANDS: none + // DESCRIPTION: Clears the expression stack. + //---------------------------------------------------------------------- + case DW_OP_APPLE_clear: + stack.clear(); + break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_APPLE_error + // OPERANDS: none + // DESCRIPTION: Pops a value off of the stack and pushed its value. + // The top item on the stack must be a variable, expression variable. + //---------------------------------------------------------------------- + case DW_OP_APPLE_error: // 0xFF - Stops expression evaluation and returns an error (no args) + if (error_ptr) + error_ptr->SetErrorString ("Generic error."); + return false; + } + } + + if (stack.empty()) + { + if (error_ptr) + error_ptr->SetErrorString ("Stack empty after evaluation."); + return false; + } + else if (log) + { + log->Printf("\n"); + size_t count = stack.size(); + for (size_t i=0; iPrintf("%s", new_value.GetData()); + } + } + + result = stack.back(); + return true; // Return true on success +} + diff --git a/lldb/source/Expression/RecordingMemoryManager.cpp b/lldb/source/Expression/RecordingMemoryManager.cpp new file mode 100644 index 000000000000..9f732b6c976d --- /dev/null +++ b/lldb/source/Expression/RecordingMemoryManager.cpp @@ -0,0 +1,131 @@ +//===-- RecordingMemoryManager.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define NO_RTTI +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Expression/RecordingMemoryManager.h" + +using namespace lldb_private; + +RecordingMemoryManager::RecordingMemoryManager () : + llvm::JITMemoryManager(), + m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()) +{ +} + +RecordingMemoryManager::~RecordingMemoryManager () +{ +} + +void +RecordingMemoryManager::setMemoryWritable () +{ + m_default_mm_ap->setMemoryWritable(); +} + +void +RecordingMemoryManager::setMemoryExecutable () +{ + m_default_mm_ap->setMemoryExecutable(); +} + + +uint8_t * +RecordingMemoryManager::startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize) +{ + uint8_t *return_value = m_default_mm_ap->startFunctionBody(F, ActualSize); + return return_value; +} + +uint8_t * +RecordingMemoryManager::allocateStub(const llvm::GlobalValue* F, unsigned StubSize, + unsigned Alignment) +{ + uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment); + m_stubs.insert (std::pair(return_value, StubSize)); + return return_value; +} + +void +RecordingMemoryManager::endFunctionBody(const llvm::Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) +{ + m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd); + m_functions.insert(std::pair(FunctionStart, FunctionEnd)); +} + +uint8_t * +RecordingMemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) +{ + uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment); + m_spaceBlocks.insert (std::pair(return_value, Size)); + return return_value; +} + +uint8_t * +RecordingMemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) +{ + uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment); + m_globals.insert (std::pair(return_value, Size)); + return return_value; +} + +void +RecordingMemoryManager::deallocateFunctionBody(void *Body) +{ + m_default_mm_ap->deallocateFunctionBody(Body); +} + +uint8_t* +RecordingMemoryManager::startExceptionTable(const llvm::Function* F, + uintptr_t &ActualSize) +{ + uint8_t *return_value = m_default_mm_ap->startExceptionTable(F, ActualSize); + return return_value; +} + +void +RecordingMemoryManager::endExceptionTable(const llvm::Function *F, uint8_t *TableStart, + uint8_t *TableEnd, uint8_t* FrameRegister) +{ + m_default_mm_ap->endExceptionTable(F, TableStart, TableEnd, FrameRegister); + m_exception_tables.insert (std::pair(TableStart, TableEnd)); +} + +void +RecordingMemoryManager::deallocateExceptionTable(void *ET) +{ + m_default_mm_ap->deallocateExceptionTable (ET); +} + +lldb::addr_t +RecordingMemoryManager::GetRemoteAddressForLocal (lldb::addr_t local_address) +{ + std::vector::iterator pos, end = m_address_map.end(); + for (pos = m_address_map.begin(); pos < end; pos++) + { + lldb::addr_t lstart = (*pos).m_local_start; + if (local_address >= lstart && local_address < lstart + (*pos).m_size) + { + return (*pos).m_remote_start + (local_address - lstart); + } + } + return LLDB_INVALID_ADDRESS; +} + +void +RecordingMemoryManager::AddToLocalToRemoteMap (lldb::addr_t lstart, size_t size, lldb::addr_t rstart) +{ + m_address_map.push_back (LocalToRemoteAddressRange(lstart, size, rstart)); +} + diff --git a/lldb/source/Host/macosx/Condition.cpp b/lldb/source/Host/macosx/Condition.cpp new file mode 100644 index 000000000000..4e93db766dbe --- /dev/null +++ b/lldb/source/Host/macosx/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 owend 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 (pthread_mutex_t *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, &abstime_ts); + } + else + err = ::pthread_cond_wait (&m_condition, mutex); + } while (err == EINTR); + + if (timed_out != NULL) + { + if (err == ETIMEDOUT) + *timed_out = true; + else + *timed_out = false; + } + + + return err; +} + diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm new file mode 100644 index 000000000000..a18d45efe812 --- /dev/null +++ b/lldb/source/Host/macosx/Host.mm @@ -0,0 +1,803 @@ +//===-- Host.mm -------------------------------------------------*- 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 + +#include +#include + +#include + +#include + +#include "CFCBundle.h" +#include "CFCReleaser.h" +#include "CFCString.h" + +#include "lldb/Host/Host.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------ +// Return the size in bytes of a page on the host system +//------------------------------------------------------------------ +size_t +Host::GetPageSize() +{ + return ::getpagesize(); +} + + +//------------------------------------------------------------------ +// Returns true if the host system is Big Endian. +//------------------------------------------------------------------ +ByteOrder +Host::GetByteOrder() +{ + union EndianTest + { + uint32_t num; + uint8_t bytes[sizeof(uint32_t)]; + } endian = { (uint16_t)0x11223344 }; + switch (endian.bytes[0]) + { + case 0x11: return eByteOrderLittle; + case 0x44: return eByteOrderBig; + case 0x33: return eByteOrderPDP; + } + return eByteOrderInvalid; +} + +lldb::pid_t +Host::GetCurrentProcessID() +{ + return ::getpid(); +} + +lldb::pid_t +Host::GetCurrentThreadID() +{ + return ::mach_thread_self(); +} + + +const ArchSpec & +Host::GetArchitecture () +{ + static ArchSpec g_host_arch; + if (!g_host_arch.IsValid()) + { + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable; + size_t len = sizeof(cputype); + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + g_host_arch.SetArch(cputype, cpusubtype); + + len = sizeof (is_64_bit_capable); + if (::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0) == 0) + { + if (is_64_bit_capable) + { + if (cputype == CPU_TYPE_I386 && cpusubtype == CPU_SUBTYPE_486) + cpusubtype = CPU_SUBTYPE_I386_ALL; + + cputype |= CPU_ARCH_ABI64; + } + } + } + } + return g_host_arch; +} + +const ConstString & +Host::GetVendorString() +{ + static ConstString g_vendor; + if (!g_vendor) + { + char ostype[64]; + size_t len = sizeof(ostype); + if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0) + g_vendor.SetCString (ostype); + } + return g_vendor; +} + +const ConstString & +Host::GetOSString() +{ + static ConstString g_os_string("apple"); + return g_os_string; +} + +const ConstString & +Host::GetTargetTriple() +{ + static ConstString g_host_triple; + if (!(g_host_triple)) + { + StreamString triple; + triple.Printf("%s-%s-%s", + GetArchitecture ().AsCString(), + GetVendorString().AsCString("apple"), + GetOSString().AsCString("darwin")); + + std::transform (triple.GetString().begin(), + triple.GetString().end(), + triple.GetString().begin(), + ::tolower); + + g_host_triple.SetCString(triple.GetString().c_str()); + } + return g_host_triple; +} + +class MacOSXDarwinThread +{ +public: + MacOSXDarwinThread(const char *thread_name) : + m_pool (nil) + { + // Register our thread with the collector if garbage collection is enabled. + if (objc_collectingEnabled()) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 + // On Leopard and earlier there is no way objc_registerThreadWithCollector + // function, so we do it manually. + auto_zone_register_thread(auto_zone()); +#else + // On SnowLoepard and later we just call the thread registration function. + objc_registerThreadWithCollector(); +#endif + } + else + { + m_pool = [[NSAutoreleasePool alloc] init]; + } + + + Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name); + } + + ~MacOSXDarwinThread() + { + if (m_pool) + [m_pool release]; + } + + static void PThreadDestructor (void *v) + { + delete (MacOSXDarwinThread*)v; + } + +protected: + NSAutoreleasePool * m_pool; +private: + DISALLOW_COPY_AND_ASSIGN (MacOSXDarwinThread); +}; + +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, MacOSXDarwinThread::PThreadDestructor); +} + +typedef 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; +} + +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 MacOSXDarwinThread(thread_name)); + } +} + +//------------------------------------------------------------------ +// Control access to a static file thread name map using a single +// static function to avoid a static constructor. +//------------------------------------------------------------------ +static const char * +ThreadNameAccessor (bool get, lldb::pid_t pid, lldb::tid_t tid, const char *name) +{ + + uint64_t pid_tid = ((uint64_t)pid << 32) | (uint64_t)tid; + + static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; + Mutex::Locker locker(&g_mutex); + + typedef std::map thread_name_map; + static thread_name_map g_thread_names; + + if (get) + { + // See if the thread name exists in our thread name pool + thread_name_map::iterator pos = g_thread_names.find(pid_tid); + if (pos != g_thread_names.end()) + return pos->second.c_str(); + } + else + { + // Set the thread name + g_thread_names[pid_tid] = name; + } + return NULL; +} + + + +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) + case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) +#else // !_POSIX_C_SOURCE + case SIGEMT: return "SIGEMT"; // 7 EMT instruction +#endif // !_POSIX_C_SOURCE + 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; +} + +const char * +Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid) +{ + const char *name = ThreadNameAccessor (true, pid, tid, NULL); + if (name == NULL) + { + // We currently can only get the name of a thread in the current process. +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + if (pid == Host::GetCurrentProcessID()) + { + char pthread_name[1024]; + if (::pthread_getname_np (::pthread_from_mach_thread_np (tid), pthread_name, sizeof(pthread_name)) == 0) + { + if (pthread_name[0]) + { + // Set the thread in our string pool + ThreadNameAccessor (false, pid, tid, pthread_name); + // Get our copy of the thread name string + name = ThreadNameAccessor (true, pid, tid, NULL); + } + } + } +#endif + } + return name; +} + +void +Host::SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name) +{ + 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 MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + // Set the pthread name if possible + if (pid == curr_pid && tid == curr_tid) + { + ::pthread_setname_np (name) == 0; + } +#endif + ThreadNameAccessor (false, pid, tid, name); +} + +FileSpec +Host::GetProgramFileSpec () +{ + static FileSpec g_program_filepsec; + if (!g_program_filepsec) + { + std::string program_fullpath; + program_fullpath.resize (PATH_MAX); + // If DST is NULL, then return the number of bytes needed. + uint32_t len = program_fullpath.size(); + int err = _NSGetExecutablePath ((char *)program_fullpath.data(), &len); + if (err < 0) + { + // The path didn't fit in the buffer provided, increase its size + // and try again + program_fullpath.resize(len); + len = program_fullpath.size(); + err = _NSGetExecutablePath ((char *)program_fullpath.data(), &len); + } + if (err == 0) + g_program_filepsec.SetFile(program_fullpath.data()); + } + return g_program_filepsec; +} + + +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); + } + return module_filespec; +} + + +bool +Host::ResolveExecutableInBundle (FileSpec *file) +{ + if (file->GetFileType () == FileSpec::eFileTypeDirectory) + { + char path[PATH_MAX]; + if (file->GetPath(path, sizeof(path))) + { + CFCBundle bundle (path); + CFCReleaser url(bundle.CopyExecutableURL ()); + if (url.get()) + { + if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path))) + { + file->SetFile(path); + return true; + } + } + } + } + return false; +} + +typedef struct MonitorInfo +{ + int handle; + pthread_t thread; + Host::MonitorChildProcessCallback callback; + void *callback_baton; + bool monitor_signals; +}; + +typedef std::multimap MonitorInfoMap; +static pthread_mutex_t g_monitor_map_mutex = PTHREAD_MUTEX_INITIALIZER; +typedef lldb::SharedPtr::Type MonitorInfoMapSP; + +static MonitorInfoMapSP& +GetMonitorMap (bool can_create) +{ + static MonitorInfoMapSP g_monitor_map_sp; + if (can_create && g_monitor_map_sp.get() == NULL) + { + g_monitor_map_sp.reset (new MonitorInfoMap); + } + return g_monitor_map_sp; +} + +static Predicate& +GetChildProcessPredicate () +{ + static Predicate g_has_child_processes; + return g_has_child_processes; +} + +static void * +MonitorChildProcessThreadFunction (void *arg); + +static pthread_t g_monitor_thread; + +uint32_t +Host::StartMonitoringChildProcess +( + MonitorChildProcessCallback callback, + void *callback_baton, + lldb::pid_t pid, + bool monitor_signals +) +{ + static uint32_t g_handle = 0; + if (callback) + { + Mutex::Locker locker(&g_monitor_map_mutex); + if (!g_monitor_thread) + { + pid_t wait_pid = -1; + g_monitor_thread = ThreadCreate ("", + MonitorChildProcessThreadFunction, + &wait_pid, + NULL); + if (g_monitor_thread) + { + //Host::ThreadDetach (g_monitor_thread, NULL); + } + } + + if (g_monitor_thread) + { + MonitorInfo info = { ++g_handle, 0, callback, callback_baton, monitor_signals }; + MonitorInfoMapSP monitor_map_sp (GetMonitorMap (true)); + if (monitor_map_sp) + { + monitor_map_sp->insert(std::make_pair(pid, info)); + GetChildProcessPredicate ().SetValue (true, eBroadcastOnChange); + return info.handle; + } + } + } + return 0; +} + +bool +Host::StopMonitoringChildProcess (uint32_t handle) +{ + Mutex::Locker locker(&g_monitor_map_mutex); + MonitorInfoMapSP monitor_map_sp (GetMonitorMap (false)); + if (monitor_map_sp) + { + MonitorInfoMap::iterator pos, end = monitor_map_sp->end(); + for (pos = monitor_map_sp->end(); pos != end; ++pos) + { + if (pos->second.handle == handle) + { + monitor_map_sp->erase(pos); + return true; + } + } + } + return false; +} + + +//------------------------------------------------------------------ +// 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); + + const pid_t wait_pid = -1;//*((pid_t*)arg); + int status = -1; + const int options = 0; + struct rusage *rusage = NULL; + while (1) + { + if (log) + log->Printf("%s ::wait4 (pid = %i, &status, options = %i, rusage = %p)...", function, wait_pid, options, rusage); + + // Wait for all child processes + ::pthread_testcancel (); + lldb::pid_t pid = ::wait4 (wait_pid, &status, options, rusage); + ::pthread_testcancel (); + + if (pid < 0) + { + // No child processes to watch wait for the mutex to be cleared + + // Scope for "locker" + { + ScopedPThreadCancelDisabler pthread_cancel_disabler; + + // First clear out all monitor entries since we have no processes + // to watch. + Mutex::Locker locker(&g_monitor_map_mutex); + // Since we don't have any child processes, we can safely clear + // anyone with a valid pid. + MonitorInfoMapSP monitor_map_sp(GetMonitorMap (false)); + if (monitor_map_sp) + { + MonitorInfoMap::iterator pos = monitor_map_sp->begin(); + while (pos != monitor_map_sp->end()) + { + // pid value of 0 and -1 are special (see man page on wait4...) + if (pos->first > 0) + { + MonitorInfoMap::iterator next_pos = pos; ++next_pos; + monitor_map_sp->erase (pos, next_pos); + pos = next_pos; + } + else + ++pos; + } + } + } + + if (log) + log->Printf("%s no child processes, wait for some...", function); + GetChildProcessPredicate ().SetValue (false, eBroadcastNever); + ::pthread_testcancel(); + GetChildProcessPredicate ().WaitForValueEqualTo (true); + if (log) + log->Printf("%s resuming monitoring of child processes.", function); + + } + else + { + ScopedPThreadCancelDisabler pthread_cancel_disabler; + 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"; + exited = true; + exit_status = -1; + } + else + { + status_cstr = "(???)"; + } + + if (log) + log->Printf ("%s ::wait4 (pid = %i, &status, options = %i, rusage = %p) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_state = %i", + function, + wait_pid, + options, + rusage, + pid, + status, + status_cstr, + signal, + exit_status); + + // Scope for mutex locker + { + // Notify anyone listening to this process + Mutex::Locker locker(&g_monitor_map_mutex); + MonitorInfoMapSP monitor_map_sp(GetMonitorMap (false)); + if (monitor_map_sp) + { + std::pair range; + range = monitor_map_sp->equal_range(pid); + MonitorInfoMap::iterator pos; + for (pos = range.first; pos != range.second; ++pos) + { + if (exited || (signal != 0 && pos->second.monitor_signals)) + { + bool callback_return = pos->second.callback (pos->second.callback_baton, pid, signal, exit_status); + + if (exited || callback_return) + { + // Make this entry as needing to be removed by + // setting its handle to zero + pos->second.handle = 0; + } + } + } + + // Remove any entries that requested to be removed or any + // entries for child processes that did exit. We know this + // because we changed the handles to an invalid value. + pos = monitor_map_sp->begin(); + while (pos != monitor_map_sp->end()) + { + if (pos->second.handle == 0) + { + MonitorInfoMap::iterator next_pos = pos; ++next_pos; + monitor_map_sp->erase (pos, next_pos); + pos = next_pos; + } + else + ++pos; + } + } + } + } + } + + if (log) + log->Printf ("ProcessMacOSX::%s (arg = %p) thread exiting...", __FUNCTION__, arg); + + g_monitor_thread = NULL; + return NULL; +} + +void +Host::WillTerminate () +{ + if (g_monitor_thread != NULL) + { + ThreadCancel (g_monitor_thread, NULL); + GetChildProcessPredicate ().SetValue (true, eBroadcastAlways); + ThreadJoin(g_monitor_thread, NULL, NULL); + g_monitor_thread = NULL; + } +} + diff --git a/lldb/source/Host/macosx/Mutex.cpp b/lldb/source/Host/macosx/Mutex.cpp new file mode 100644 index 000000000000..ab0b67aa0511 --- /dev/null +++ b/lldb/source/Host/macosx/Mutex.cpp @@ -0,0 +1,244 @@ +//===-- 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/Core/Log.h" + +#if 0 +#include "lldb/Host/Host.h" +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#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(m.GetMutex()) +{ + if (m_mutex_ptr) + Mutex::Lock (m_mutex_ptr); +} + +//---------------------------------------------------------------------- +// 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(m ? m->GetMutex() : NULL) +{ + if (m_mutex_ptr) + Mutex::Lock (m_mutex_ptr); +} + +//---------------------------------------------------------------------- +// Constructor with a raw pthread mutex object pointer. +// +// This will create a scoped mutex locking object that locks "mutex" +//---------------------------------------------------------------------- +Mutex::Locker::Locker (pthread_mutex_t *mutex_ptr) : + m_mutex_ptr(mutex_ptr) +{ + if (m_mutex_ptr) + Mutex::Lock (m_mutex_ptr); +} + +//---------------------------------------------------------------------- +// Desstructor +// +// Unlocks any owned mutex object (if it is valid). +//---------------------------------------------------------------------- +Mutex::Locker::~Locker () +{ + if (m_mutex_ptr) + Mutex::Unlock (m_mutex_ptr); +} + +//---------------------------------------------------------------------- +// 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::Reset (pthread_mutex_t *mutex_ptr) +{ + if (m_mutex_ptr) + Mutex::Unlock (m_mutex_ptr); + + m_mutex_ptr = mutex_ptr; + if (m_mutex_ptr) + Mutex::Lock (m_mutex_ptr); +} + +bool +Mutex::Locker::TryLock (pthread_mutex_t *mutex_ptr) +{ + if (m_mutex_ptr) + Mutex::Unlock (m_mutex_ptr); + m_mutex_ptr = NULL; + if (mutex_ptr) + { + if (Mutex::TryLock (mutex_ptr) == 0) + m_mutex_ptr = mutex_ptr; + } + 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); + 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: + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL); + break; + + case eMutexTypeRecursive: + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + break; + + default: + err = -1; + break; + } + assert(err == 0); + err = ::pthread_mutex_init (&m_mutex, &attr); + assert(err == 0); + err = ::pthread_mutexattr_destroy (&attr); + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Destructor. +// +// Destroys the mutex owned by this object. +//---------------------------------------------------------------------- +Mutex::~Mutex() +{ + int err; + err = ::pthread_mutex_destroy (&m_mutex); +} + +//---------------------------------------------------------------------- +// Mutex get accessor. +//---------------------------------------------------------------------- +pthread_mutex_t * +Mutex::GetMutex() +{ + return &m_mutex; +} + +int +Mutex::Lock (pthread_mutex_t *mutex_ptr) +{ + DEBUG_LOG ("[%4.4x/%4.4x] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), mutex_ptr); + int err = ::pthread_mutex_lock (mutex_ptr); + DEBUG_LOG ("[%4.4x/%4.4x] pthread_mutex_lock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), mutex_ptr, err); + return err; +} + +int +Mutex::TryLock (pthread_mutex_t *mutex_ptr) +{ + int err = ::pthread_mutex_trylock (mutex_ptr); + DEBUG_LOG ("[%4.4x/%4.4x] pthread_mutex_trylock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), mutex_ptr, err); + return err; +} + +int +Mutex::Unlock (pthread_mutex_t *mutex_ptr) +{ + int err = ::pthread_mutex_unlock (mutex_ptr); + DEBUG_LOG ("[%4.4x/%4.4x] pthread_mutex_unlock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), mutex_ptr, err); + return err; +} + +//---------------------------------------------------------------------- +// 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() +{ + return Mutex::Lock (&m_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. +// +// RETURNS +// The error code from the pthread_mutex_trylock() function call. +//---------------------------------------------------------------------- +int +Mutex::TryLock() +{ + return Mutex::TryLock (&m_mutex); +} + +//---------------------------------------------------------------------- +// 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() +{ + return Mutex::Unlock (&m_mutex); +} diff --git a/lldb/source/Host/macosx/Symbols.cpp b/lldb/source/Host/macosx/Symbols.cpp new file mode 100644 index 000000000000..ff28157258b7 --- /dev/null +++ b/lldb/source/Host/macosx/Symbols.cpp @@ -0,0 +1,462 @@ +//===-- 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" + +// C Includes +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +#include + +// Project includes +#include "CFCReleaser.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" + +using namespace lldb; +using namespace lldb_private; + +extern "C" { +CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); +CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); +}; + +static bool +SkinnyMachOFileContainsArchAndUUID +( + const FileSpec &file_spec, + const ArchSpec *arch, + const UUID *uuid, // the UUID we are looking for + off_t file_offset, + DataExtractor& data, + uint32_t data_offset, + const uint32_t magic +) +{ + assert(magic == MH_CIGAM || magic == MH_MAGIC || magic == MH_CIGAM_64 || magic == MH_MAGIC_64); + if (magic == MH_MAGIC || magic == MH_MAGIC_64) + data.SetByteOrder (eByteOrderHost); + else if (eByteOrderHost == eByteOrderBig) + data.SetByteOrder (eByteOrderLittle); + else + data.SetByteOrder (eByteOrderBig); + + uint32_t i; + const uint32_t cputype = data.GetU32(&data_offset); // cpu specifier + const uint32_t cpusubtype = data.GetU32(&data_offset); // machine specifier + data_offset+=4; // Skip mach file type + const uint32_t ncmds = data.GetU32(&data_offset); // number of load commands + const uint32_t sizeofcmds = data.GetU32(&data_offset); // the size of all the load commands + data_offset+=4; // Skip flags + + // Check the architecture if we have a valid arch pointer + if (arch) + { + ArchSpec file_arch(cputype, cpusubtype); + + if (file_arch != *arch) + return false; + } + + // The file exists, and if a valid arch pointer was passed in we know + // if already matches, so we can return if we aren't looking for a specific + // UUID + if (uuid == NULL) + return true; + + if (magic == MH_CIGAM_64 || magic == MH_MAGIC_64) + data_offset += 4; // Skip reserved field for in mach_header_64 + + // Make sure we have enough data for all the load commands + if (magic == MH_CIGAM_64 || magic == MH_MAGIC_64) + { + if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds) + { + DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds)); + data.SetData (data_buffer_sp); + } + } + else + { + if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds) + { + DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds)); + data.SetData (data_buffer_sp); + } + } + + for (i=0; iGetByteSize() > 0) + { + data.SetData(data_buffer_sp); + + uint32_t data_offset = 0; + uint32_t magic = data.GetU32(&data_offset); + + switch (magic) + { + // 32 bit mach-o file + case MH_CIGAM: + case MH_MAGIC: + case MH_CIGAM_64: + case MH_MAGIC_64: + return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); + + // fat mach-o file + case FAT_CIGAM: + case FAT_MAGIC: + return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); + + default: + break; + } + } + return false; +} + +static FileSpec +LocateDSYMMachFileInDSYMBundle +( + const FileSpec& dsym_bundle_fspec, + const UUID *uuid, + const ArchSpec *arch) +{ + char path[PATH_MAX]; + + FileSpec dsym_fspec; + + if (dsym_bundle_fspec.GetPath(path, sizeof(path))) + { + ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); + + DIR* dirp = ::opendir(path); + if (dirp != NULL) + { + const size_t path_len = strlen(path); + const int bytes_left = sizeof(path) - path_len - 1; + struct dirent* dp; + while ((dp = readdir(dirp)) != NULL) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + if (dp->d_namlen == 1 && dp->d_name[0] == '.') + continue; + + if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) + { + ::strncpy (&path[path_len], dp->d_name, bytes_left); + + dsym_fspec.SetFile(path); + if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) + return dsym_fspec; + } + } + } + } + dsym_fspec.Clear(); + return dsym_fspec; +} + +static int +LocateMacOSXFilesUsingDebugSymbols +( + const FileSpec *exec_fspec, // An executable path that may or may not be correct if UUID is specified + const ArchSpec* arch, // Limit the search to files with this architecture if non-NULL + const UUID *uuid, // Match the UUID value if non-NULL, + FileSpec *out_exec_fspec, // If non-NULL, try and find the executable + FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file +) +{ + int items_found = 0; + + if (out_exec_fspec) + out_exec_fspec->Clear(); + + if (out_dsym_fspec) + out_dsym_fspec->Clear(); + + if (uuid && uuid->IsValid()) + { + // Try and locate the dSYM file using DebugSymbols first + const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); + if (module_uuid != NULL) + { + CFCReleaser module_uuid_ref(::CFUUIDCreateWithBytes ( NULL, + module_uuid[0], + module_uuid[1], + module_uuid[2], + module_uuid[3], + module_uuid[4], + module_uuid[5], + module_uuid[6], + module_uuid[7], + module_uuid[8], + module_uuid[9], + module_uuid[10], + module_uuid[11], + module_uuid[12], + module_uuid[13], + module_uuid[14], + module_uuid[15])); + + if (module_uuid_ref.get()) + { + CFCReleaser exec_url; + + if (exec_fspec) + { + char exec_cf_path[PATH_MAX]; + if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) + exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, + (const UInt8 *)exec_cf_path, + strlen(exec_cf_path), + FALSE)); + } + + CFCReleaser dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); + char path[PATH_MAX]; + + if (dsym_url.get()) + { + if (out_dsym_fspec) + { + if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) + { + out_dsym_fspec->SetFile(path); + + if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory) + { + *out_dsym_fspec = LocateDSYMMachFileInDSYMBundle (*out_dsym_fspec, uuid, arch); + if (*out_dsym_fspec) + ++items_found; + } + else + { + ++items_found; + } + } + } + + if (out_exec_fspec) + { + CFCReleaser dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));; + if (dict.get()) + { + CFStringRef exec_cf_path = static_cast(::CFDictionaryGetValue (dict.get(), CFSTR("DBGSymbolRichExecutable"))); + if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) + { + ++items_found; + out_dsym_fspec->SetFile(path); + } + } + } + } + } + } + } + return items_found; +} + +static bool +LocateDSYMInVincinityOfExecutable (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid, FileSpec &dsym_fspec) +{ + if (exec_fspec) + { + char path[PATH_MAX]; + if (exec_fspec->GetPath(path, sizeof(path))) + { + // Make sure the module isn't already just a dSYM file... + if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) + { + size_t obj_file_path_length = strlen(path); + strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); + strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); + + dsym_fspec.SetFile(path); + + if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) + { + return true; + } + else + { + path[obj_file_path_length] = '\0'; + + char *last_dot = strrchr(path, '.'); + while (last_dot != NULL && last_dot[0]) + { + char *next_slash = strchr(last_dot, '/'); + if (next_slash != NULL) + { + *next_slash = '\0'; + strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); + strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); + dsym_fspec.SetFile(path); + if (dsym_fspec.Exists()) + return true; + else + { + *last_dot = '\0'; + char *prev_slash = strrchr(path, '/'); + if (prev_slash != NULL) + *prev_slash = '\0'; + else + break; + } + } + else + { + break; + } + } + } + } + } + } + dsym_fspec.Clear(); + return false; +} + +FileSpec +Symbols::LocateExecutableObjectFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", + exec_fspec ? exec_fspec->GetFilename().AsCString ("") : "", + arch ? arch->AsCString() : "", + uuid); + + FileSpec objfile_fspec; + if (exec_fspec && FileAtPathContainsArchAndUUID (*exec_fspec, arch, uuid)) + objfile_fspec = *exec_fspec; + else + LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, &objfile_fspec, NULL); + return objfile_fspec; +} + +FileSpec +Symbols::LocateExecutableSymbolFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)", + exec_fspec ? exec_fspec->GetFilename().AsCString ("") : "", + arch ? arch->AsCString() : "", + uuid); + + FileSpec symbol_fspec; + // First try and find the dSYM in the same directory as the executable or in + // an appropriate parent directory + if (LocateDSYMInVincinityOfExecutable (exec_fspec, arch, uuid, symbol_fspec) == false) + { + // We failed to easily find the dSYM above, so use DebugSymbols + LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, NULL, &symbol_fspec); + } + return symbol_fspec; +} diff --git a/lldb/source/Host/macosx/TimeValue.cpp b/lldb/source/Host/macosx/TimeValue.cpp new file mode 100644 index 000000000000..27aad5f7bc7d --- /dev/null +++ b/lldb/source/Host/macosx/TimeValue.cpp @@ -0,0 +1,179 @@ +//===-- 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 "TimeValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#define NSEC_PER_USEC 1000ull +#define USEC_PER_SEC 1000000ull +#define NSEC_PER_SEC 1000000000ull + +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 (ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec) +{ +} + +TimeValue::TimeValue(const struct timeval& tv) : + m_nano_seconds (tv.tv_sec * NSEC_PER_SEC + tv.tv_usec * NSEC_PER_USEC) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TimeValue::~TimeValue() +{ +} + + +uint64_t +TimeValue::GetAsNanoSecondsSinceJan1_1970() const +{ + return m_nano_seconds; +} + +uint64_t +TimeValue::GetAsMicroSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NSEC_PER_USEC; +} + +struct timespec +TimeValue::GetAsTimeSpec () const +{ + struct timespec ts; + ts.tv_sec = m_nano_seconds / NSEC_PER_SEC; + ts.tv_nsec = m_nano_seconds % NSEC_PER_SEC; + return ts; +} + +struct timeval +TimeValue::GetAsTimeVal () const +{ + struct timeval tv; + tv.tv_sec = m_nano_seconds / NSEC_PER_SEC; + tv.tv_usec = (m_nano_seconds % NSEC_PER_SEC) / NSEC_PER_USEC; + return tv; +} + +void +TimeValue::Clear () +{ + m_nano_seconds = 0; +} + +bool +TimeValue::IsValid () const +{ + return m_nano_seconds != 0; +} + +void +TimeValue::OffsetWithSeconds (uint32_t sec) +{ + m_nano_seconds += sec * NSEC_PER_SEC; +} + +void +TimeValue::OffsetWithMicroSeconds (uint32_t usec) +{ + m_nano_seconds += usec * NSEC_PER_USEC; +} + +void +TimeValue::OffsetWithNanoSeconds (uint32_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; +} + + +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/lldb/source/Host/macosx/cfcpp/CFCBundle.cpp b/lldb/source/Host/macosx/cfcpp/CFCBundle.cpp new file mode 100644 index 000000000000..6e68af5c597a --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCBundle.cpp @@ -0,0 +1,83 @@ +//===-- CFCBundle.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCBundle.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCBundle constructor +//---------------------------------------------------------------------- +CFCBundle::CFCBundle(const char *path) : + CFCReleaser() +{ + if (path && path[0]) + SetPath(path); +} + +CFCBundle::CFCBundle(CFURLRef url) : + CFCReleaser(url ? CFBundleCreate(NULL, url) : NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCBundle::~CFCBundle() +{ +} + +//---------------------------------------------------------------------- +// Set the path for a bundle by supplying a +//---------------------------------------------------------------------- +bool +CFCBundle::SetPath (const char *path) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + // Release our old bundle and URL + reset(); + + // Make a CFStringRef from the supplied path + CFCString cf_path; + cf_path.SetFileSystemRepresentation(path); + if (cf_path.get()) + { + // Make our Bundle URL + CFCReleaser bundle_url (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true)); + if (bundle_url.get()) + reset (::CFBundleCreate (alloc, bundle_url.get())); + } + return get() != NULL; +} + +CFStringRef +CFCBundle::GetIdentifier () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetIdentifier (bundle); + return NULL; +} + +CFTypeRef +CFCBundle::GetValueForInfoDictionaryKey(CFStringRef key) const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetValueForInfoDictionaryKey(bundle, key); + return NULL; +} + +CFURLRef +CFCBundle::CopyExecutableURL () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return CFBundleCopyExecutableURL(bundle); + return NULL; +} diff --git a/lldb/source/Host/macosx/cfcpp/CFCBundle.h b/lldb/source/Host/macosx/cfcpp/CFCBundle.h new file mode 100644 index 000000000000..c07a48cb0553 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCBundle.h @@ -0,0 +1,47 @@ +//===-- CFCBundle.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFBundle_h_ +#define CoreFoundationCPP_CFBundle_h_ + +#include "CFCReleaser.h" + +class CFCBundle : public CFCReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCBundle (const char *path = NULL); + CFCBundle (CFURLRef url); + + virtual + ~CFCBundle(); + + CFURLRef + CopyExecutableURL () const; + + CFStringRef + GetIdentifier () const; + + CFTypeRef + GetValueForInfoDictionaryKey(CFStringRef key) const; + + bool + SetPath (const char *path); + +private: + // Disallow copy and assignment constructors + CFCBundle(const CFCBundle&); + + const CFCBundle& + operator=(const CFCBundle&); +}; + +#endif // #ifndef CoreFoundationCPP_CFBundle_h_ diff --git a/lldb/source/Host/macosx/cfcpp/CFCData.cpp b/lldb/source/Host/macosx/cfcpp/CFCData.cpp new file mode 100644 index 000000000000..4f49368ad8ad --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCData.cpp @@ -0,0 +1,82 @@ +//===-- CFCData.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCData.h" + +//---------------------------------------------------------------------- +// CFCData constructor +//---------------------------------------------------------------------- +CFCData::CFCData(CFDataRef data) : + CFCReleaser(data) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData::CFCData(const CFCData& rhs) : + CFCReleaser(rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData& +CFCData::operator=(const CFCData& rhs) + +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCData::~CFCData() +{ +} + + +CFIndex +CFCData::GetLength() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetLength (data); + return 0; +} + + +const uint8_t* +CFCData::GetBytePtr() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetBytePtr (data); + return NULL; +} + +CFDataRef +CFCData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + reset(); + CFCReleaser stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc)); + ::CFWriteStreamOpen (stream.get()); + CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL); + if (len > 0) + reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten)); + ::CFWriteStreamClose (stream.get()); + return get(); +} + diff --git a/lldb/source/Host/macosx/cfcpp/CFCData.h b/lldb/source/Host/macosx/cfcpp/CFCData.h new file mode 100644 index 000000000000..6a718f54c055 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCData.h @@ -0,0 +1,35 @@ +//===-- CFCData.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFData_h_ +#define CoreFoundationCPP_CFData_h_ + +#include "CFCReleaser.h" + +class CFCData : public CFCReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCData(CFDataRef data = NULL); + CFCData(const CFCData& rhs); + CFCData& operator=(const CFCData& rhs); + virtual ~CFCData(); + + CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format); + const uint8_t* GetBytePtr () const; + CFIndex GetLength () const; +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCData can see and modify these + //------------------------------------------------------------------ +}; + +#endif // #ifndef CoreFoundationCPP_CFData_h_ diff --git a/lldb/source/Host/macosx/cfcpp/CFCMutableArray.cpp b/lldb/source/Host/macosx/cfcpp/CFCMutableArray.cpp new file mode 100644 index 000000000000..3b92f03fa614 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCMutableArray.cpp @@ -0,0 +1,123 @@ +//===-- CFCMutableArray.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableArray.h" + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(CFMutableArrayRef s) : + CFCReleaser (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(const CFCMutableArray& rhs) : + CFCReleaser (rhs) // NOTE: this won't make a copy of the array, just add a new reference to it +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray& +CFCMutableArray::operator=(const CFCMutableArray& rhs) +{ + if (this != &rhs) + *this = rhs; // NOTE: this operator won't make a copy of the array, just add a new reference to it + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableArray::~CFCMutableArray() +{ +} + + +CFIndex +CFCMutableArray::GetCount() const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCount (array); + return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(CFRange range, const void *value) const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCountOfValue (array, range, value); + return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(const void *value) const +{ + CFMutableArrayRef array = get(); + if (array) + return ::CFArrayGetCountOfValue (array, CFRangeMake(0, GetCount()), value); + return 0; +} + +const void * +CFCMutableArray::GetValueAtIndex(CFIndex idx) const +{ + CFMutableArrayRef array = get(); + if (array) + { + const CFIndex num_array_items = ::CFArrayGetCount (array); + if (0 <= idx && idx < num_array_items) + { + return ::CFArrayGetValueAtIndex (array, idx); + } + } + return NULL; +} + +bool +CFCMutableArray::SetValueAtIndex(CFIndex idx, const void *value) +{ + CFMutableArrayRef array = get(); + if (array != NULL) + { + const CFIndex num_array_items = ::CFArrayGetCount (array); + if (0 <= idx && idx < num_array_items) + { + ::CFArraySetValueAtIndex (array, idx, value); + return true; + } + } + return false; +} + + +bool +CFCMutableArray::AppendValue(const void *value, bool can_create) +{ + CFMutableArrayRef array = get(); + if (array == NULL) + { + if (can_create == false) + return false; + array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + reset ( array ); + } + if (array != NULL) + { + ::CFArrayAppendValue(array, value); + return true; + } + return false; +} diff --git a/lldb/source/Host/macosx/cfcpp/CFCMutableArray.h b/lldb/source/Host/macosx/cfcpp/CFCMutableArray.h new file mode 100644 index 000000000000..eaadb8d55907 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCMutableArray.h @@ -0,0 +1,34 @@ +//===-- CFCMutableArray.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableArray_h_ +#define CoreFoundationCPP_CFMutableArray_h_ + +#include "CFCReleaser.h" + +class CFCMutableArray : public CFCReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableArray(CFMutableArrayRef array = NULL); + CFCMutableArray(const CFCMutableArray& rhs); // This will copy the array contents into a new array + CFCMutableArray& operator=(const CFCMutableArray& rhs); // This will re-use the same array and just bump the ref count + virtual ~CFCMutableArray(); + + CFIndex GetCount() const; + CFIndex GetCountOfValue(const void *value) const; + CFIndex GetCountOfValue(CFRange range, const void *value) const; + const void * GetValueAtIndex(CFIndex idx) const; + bool SetValueAtIndex(CFIndex idx, const void *value); + bool AppendValue(const void *value, bool can_create = true); // Appends value and optionally creates a CFCMutableArray if this class doesn't contain one +}; + +#endif // #ifndef CoreFoundationCPP_CFMutableArray_h_ diff --git a/lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp b/lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp new file mode 100644 index 000000000000..963221adb4a8 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp @@ -0,0 +1,491 @@ +//===-- CFCMutableDictionary.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableDictionary.h" +#include "CFCString.h" +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(CFMutableDictionaryRef s) : + CFCReleaser (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(const CFCMutableDictionary& rhs) : + CFCReleaser (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +const CFCMutableDictionary& +CFCMutableDictionary::operator=(const CFCMutableDictionary& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableDictionary::~CFCMutableDictionary() +{ +} + + +CFIndex +CFCMutableDictionary::GetCount() const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCount (dict); + return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfKey(const void *key) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCountOfKey (dict, key); + return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfValue(const void *value) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetCountOfValue (dict, value); + return 0; +} + +void +CFCMutableDictionary::GetKeysAndValues(const void **keys, const void **values) const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryGetKeysAndValues (dict, keys, values); +} + + +const void * +CFCMutableDictionary::GetValue(const void *key) const + +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetValue (dict, key); + return NULL; +} + +Boolean +CFCMutableDictionary::GetValueIfPresent(const void *key, const void **value_handle) const +{ + CFMutableDictionaryRef dict = get(); + if (dict) + return ::CFDictionaryGetValueIfPresent (dict, key, value_handle); + return false; +} + + +CFMutableDictionaryRef +CFCMutableDictionary::Dictionary(bool can_create) +{ + CFMutableDictionaryRef dict = get(); + if (can_create && dict == NULL) + { + dict = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + reset ( dict ); + } + return dict; +} + +bool +CFCMutableDictionary::AddValue(CFStringRef key, const void *value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, value); + return true; + } + return false; +} + +bool +CFCMutableDictionary::SetValue(CFStringRef key, const void *value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, value); + return true; + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int16_t sval = value; + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int16_t sval = value; + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::AddValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int32_t sval = value; + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int32_t sval = value; + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int64_t sval = value; + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // Have to promote to the next size type so things don't appear negative of the MSBit is set... + int64_t sval = value; + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::AddValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + + +bool +CFCMutableDictionary::SetValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + // The number may appear negative if the MSBit is set in "value". Due to a limitation of + // CFNumber, there isn't a way to have it show up otherwise as of this writing. + CFCReleaser cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + if (cf_number.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_number.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::AddValueCString(CFStringRef key, const char *cstr, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCString cf_str(cstr, kCFStringEncodingUTF8); + if (cf_str.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionaryAddValue (dict, key, cf_str.get()); + return true; + } + } + return false; +} + +bool +CFCMutableDictionary::SetValueCString(CFStringRef key, const char *cstr, bool can_create) +{ + CFMutableDictionaryRef dict = Dictionary(can_create); + if (dict != NULL) + { + CFCString cf_str(cstr, kCFStringEncodingUTF8); + if (cf_str.get()) + { + // Let the dictionary own the CFNumber + ::CFDictionarySetValue (dict, key, cf_str.get()); + return true; + } + } + return false; +} + + +void +CFCMutableDictionary::RemoveAllValues() +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryRemoveAllValues(dict); +} + +void +CFCMutableDictionary::RemoveValue(const void *value) +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryRemoveValue(dict, value); +} +void +CFCMutableDictionary::ReplaceValue(const void *key, const void *value) +{ + CFMutableDictionaryRef dict = get(); + if (dict) + ::CFDictionaryReplaceValue (dict, key, value); +} + diff --git a/lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.h b/lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.h new file mode 100644 index 000000000000..de32ead3103f --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCMutableDictionary.h @@ -0,0 +1,77 @@ +//===-- CFCMutableDictionary.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableDictionary_h_ +#define CoreFoundationCPP_CFMutableDictionary_h_ + +#include "CFCReleaser.h" + +class CFCMutableDictionary : public CFCReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableDictionary(CFMutableDictionaryRef s = NULL); + CFCMutableDictionary(const CFCMutableDictionary& rhs); + virtual ~CFCMutableDictionary(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const CFCMutableDictionary& + operator=(const CFCMutableDictionary& rhs); + + + CFIndex GetCount() const; + CFIndex GetCountOfKey(const void *value) const; + CFIndex GetCountOfValue(const void *value) const; + void GetKeysAndValues(const void **keys, const void **values) const; + const void * GetValue(const void *key) const; + Boolean GetValueIfPresent(const void *key, const void **value_handle) const; + bool AddValue(CFStringRef key, const void *value, bool can_create = false); + bool SetValue(CFStringRef key, const void *value, bool can_create = false); + bool AddValueSInt8(CFStringRef key, int8_t value, bool can_create = false); + bool SetValueSInt8(CFStringRef key, int8_t value, bool can_create = false); + bool AddValueSInt16(CFStringRef key, int16_t value, bool can_create = false); + bool SetValueSInt16(CFStringRef key, int16_t value, bool can_create = false); + bool AddValueSInt32(CFStringRef key, int32_t value, bool can_create = false); + bool SetValueSInt32(CFStringRef key, int32_t value, bool can_create = false); + bool AddValueSInt64(CFStringRef key, int64_t value, bool can_create = false); + bool SetValueSInt64(CFStringRef key, int64_t value, bool can_create = false); + bool AddValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); + bool SetValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); + bool AddValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); + bool SetValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); + bool AddValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); + bool SetValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); + bool AddValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); + bool SetValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); + bool AddValueCString(CFStringRef key, const char *cstr, bool can_create = false); + bool SetValueCString(CFStringRef key, const char *cstr, bool can_create = false); + void RemoveValue(const void *value); + void ReplaceValue(const void *key, const void *value); + void RemoveAllValues(); + CFMutableDictionaryRef Dictionary(bool can_create); + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCMutableDictionary can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For CFCMutableDictionary only + //------------------------------------------------------------------ + +}; + + +#endif // CoreFoundationCPP_CFMutableDictionary_h_ diff --git a/lldb/source/Host/macosx/cfcpp/CFCMutableSet.cpp b/lldb/source/Host/macosx/cfcpp/CFCMutableSet.cpp new file mode 100644 index 000000000000..cd253704e2d1 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCMutableSet.cpp @@ -0,0 +1,114 @@ +//===-- CFCMutableSet.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableSet.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(CFMutableSetRef s) : + CFCReleaser (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(const CFCMutableSet& rhs) : + CFCReleaser (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +const CFCMutableSet& +CFCMutableSet::operator=(const CFCMutableSet& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableSet::~CFCMutableSet() +{ +} + + +CFIndex +CFCMutableSet::GetCount() const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetCount (set); + return 0; +} + +CFIndex +CFCMutableSet::GetCountOfValue(const void *value) const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetCountOfValue (set, value); + return 0; +} + +const void * +CFCMutableSet::GetValue(const void *value) const +{ + CFMutableSetRef set = get(); + if (set) + return ::CFSetGetValue(set, value); + return NULL; +} + + +const void * +CFCMutableSet::AddValue(const void *value, bool can_create) +{ + CFMutableSetRef set = get(); + if (set == NULL) + { + if (can_create == false) + return false; + set = ::CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); + reset ( set ); + } + if (set != NULL) + { + ::CFSetAddValue(set, value); + return value; + } + return NULL; +} + +void +CFCMutableSet::RemoveValue(const void *value) +{ + CFMutableSetRef set = get(); + if (set) + ::CFSetRemoveValue(set, value); +} + +void +CFCMutableSet::RemoveAllValues() +{ + CFMutableSetRef set = get(); + if (set) + ::CFSetRemoveAllValues(set); +} + diff --git a/lldb/source/Host/macosx/cfcpp/CFCMutableSet.h b/lldb/source/Host/macosx/cfcpp/CFCMutableSet.h new file mode 100644 index 000000000000..78f7a8be81d2 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCMutableSet.h @@ -0,0 +1,53 @@ +//===-- CFCMutableSet.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableSet_h_ +#define CoreFoundationCPP_CFMutableSet_h_ + +#include "CFCReleaser.h" + +class CFCMutableSet : public CFCReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCMutableSet(CFMutableSetRef s = NULL); + CFCMutableSet(const CFCMutableSet& rhs); + virtual ~CFCMutableSet(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const CFCMutableSet& + operator=(const CFCMutableSet& rhs); + + + CFIndex GetCount() const; + CFIndex GetCountOfValue(const void *value) const; + const void * GetValue(const void *value) const; + const void * AddValue(const void *value, bool can_create); + void RemoveValue(const void *value); + void RemoveAllValues(); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFCMutableSet can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For CFCMutableSet only + //------------------------------------------------------------------ + +}; + +#endif // CoreFoundationCPP_CFMutableSet_h_ diff --git a/lldb/source/Host/macosx/cfcpp/CFCReleaser.h b/lldb/source/Host/macosx/cfcpp/CFCReleaser.h new file mode 100644 index 000000000000..cd35de6d665d --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCReleaser.h @@ -0,0 +1,155 @@ +//===-- CFCReleaser.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFReleaser_h_ +#define CoreFoundationCPP_CFReleaser_h_ + +#include + +#ifdef __cplusplus + +#include + +//---------------------------------------------------------------------- +// Templatized CF helper class that can own any CF pointer and will +// call CFRelease() on any valid pointer it owns unless that pointer is +// explicitly released using the release() member function. This class +// is designed to mimic the std::auto_ptr class and has all of the +// same functions. The one thing to watch out for is the +// CFCReleaser::release() function won't actually CFRelease any owned +// pointer, it is designed to relinquish ownwership of the pointer just +// like std:auto_ptr::release() does. +//---------------------------------------------------------------------- +template +class CFCReleaser +{ +public: + //---------------------------------------------------------- + // Constructor that takes a pointer to a CF object that is + // to be released when this object goes out of scope + //---------------------------------------------------------- + CFCReleaser(T ptr = NULL) : + _ptr(ptr) + { + } + + //---------------------------------------------------------- + // Copy constructor + // + // Note that copying a CFCReleaser will not transfer + // ownership of the contained pointer, but it will bump its + // reference count. This is where this class differs from + // std::auto_ptr. + //---------------------------------------------------------- + CFCReleaser(const CFCReleaser& rhs) : + _ptr(rhs.get()) + { + if (get()) + ::CFRetain(get()); + } + + + //---------------------------------------------------------- + // The destructor will release the pointer that it contains + // if it has a valid pointer. + //---------------------------------------------------------- + virtual ~CFCReleaser() + { + reset(); + } + + //---------------------------------------------------------- + // Assignment operator. + // + // Note that assigning one CFCReleaser to another will + // not transfer ownership of the contained pointer, but it + // will bump its reference count. This is where this class + // differs from std::auto_ptr. + //---------------------------------------------------------- + CFCReleaser& + operator= (const CFCReleaser& rhs) + { + if (this != &rhs) + { + // Replace our owned pointer with the new one + reset(rhs.get()); + // Retain the current pointer that we own + if (get()) + ::CFRetain(get()); + } + return *this; + } + + //---------------------------------------------------------- + // Get the address of the contained type in case it needs + // to be passed to a function that will fill in a pointer + // value. The function currently will assert if _ptr is not + // NULL because the only time this method should be used is + // if another function will modify the contents, and we + // could leak a pointer if this is not NULL. If the + // assertion fires, check the offending code, or call + // reset() prior to using the "ptr_address()" member to make + // sure any owned objects has CFRelease called on it. + //---------------------------------------------------------- + T* + ptr_address() + { + assert (_ptr == NULL); + return &_ptr; + } + + //---------------------------------------------------------- + // Access the pointer itself + //---------------------------------------------------------- + T + get() + { + return _ptr; + } + + const T + get() const + { + return _ptr; + } + + + //---------------------------------------------------------- + // Set a new value for the pointer and CFRelease our old + // value if we had a valid one. + //---------------------------------------------------------- + void + reset(T ptr = NULL) + { + if ((_ptr != NULL) && (ptr != _ptr)) + ::CFRelease(_ptr); + _ptr = ptr; + } + + //---------------------------------------------------------- + // Release ownership without calling CFRelease. This class + // is designed to mimic std::auto_ptr, so the release + // method releases ownership of the contained pointer + // and does NOT call CFRelease. + //---------------------------------------------------------- + T + release() + { + T tmp = _ptr; + _ptr = NULL; + return tmp; + } + +private: + T _ptr; +}; + +#endif // #ifdef __cplusplus +#endif // #ifndef CoreFoundationCPP_CFReleaser_h_ + diff --git a/lldb/source/Host/macosx/cfcpp/CFCString.cpp b/lldb/source/Host/macosx/cfcpp/CFCString.cpp new file mode 100644 index 000000000000..81a96b824999 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCString.cpp @@ -0,0 +1,195 @@ +//===-- CFCString.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCString.h" +#include +#include + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCString::CFCString(CFStringRef s) : + CFCReleaser (s) +{ +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString::CFCString(const CFCString& rhs) : + CFCReleaser (rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString& +CFCString::operator=(const CFCString& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +CFCString::CFCString (const char *cstr, CFStringEncoding cstr_encoding) : + CFCReleaser () +{ + if (cstr && cstr[0]) + { + reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding)); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCString::~CFCString() +{ +} + +const char * +CFCString::GetFileSystemRepresentation(std::string& s) +{ + return CFCString::FileSystemRepresentation(get(), s); +} + +CFStringRef +CFCString::SetFileSystemRepresentation (const char *path) +{ + CFStringRef new_value = NULL; + if (path && path[0]) + new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path); + reset(new_value); + return get(); +} + + +CFStringRef +CFCString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type) +{ + CFStringRef new_value = NULL; + if (cf_type != NULL) + { + CFTypeID cf_type_id = ::CFGetTypeID(cf_type); + + if (cf_type_id == ::CFStringGetTypeID()) + { + // Retain since we are using the existing object + new_value = (CFStringRef)::CFRetain(cf_type); + } + else if (cf_type_id == ::CFURLGetTypeID()) + { + new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle); + } + } + reset(new_value); + return get(); +} + +CFStringRef +CFCString::SetFileSystemRepresentationAndExpandTilde (const char *path) +{ + std::string expanded_path; + if (CFCString::ExpandTildeInPath(path, expanded_path)) + SetFileSystemRepresentation(expanded_path.c_str()); + else + reset(); + return get(); +} + +const char * +CFCString::UTF8(std::string& str) +{ + return CFCString::UTF8(get(), str); +} + +// Static function that puts a copy of the UTF8 contents of CF_STR into STR +// and returns the C string pointer that is contained in STR when successful, else +// NULL is returned. This allows the std::string parameter to own the extracted string, +// and also allows that string to be returned as a C string pointer that can be used. + +const char * +CFCString::UTF8 (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + const CFStringEncoding encoding = kCFStringEncodingUTF8; + CFIndex max_utf8_str_len = CFStringGetLength (cf_str); + max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding); + if (max_utf8_str_len > 0) + { + str.resize(max_utf8_str_len); + if (!str.empty()) + { + if (CFStringGetCString (cf_str, &str[0], str.size(), encoding)) + { + str.resize(strlen(str.c_str())); + return str.c_str(); + } + } + } + } + return NULL; +} + +const char* +CFCString::ExpandTildeInPath(const char* path, std::string &expanded_path) +{ + glob_t globbuf; + if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0) + { + expanded_path = globbuf.gl_pathv[0]; + ::globfree (&globbuf); + } + else + expanded_path.clear(); + + return expanded_path.c_str(); +} + +// Static function that puts a copy of the file system representation of CF_STR +// into STR and returns the C string pointer that is contained in STR when +// successful, else NULL is returned. This allows the std::string parameter +// to own the extracted string, and also allows that string to be returned as +// a C string pointer that can be used. + +const char * +CFCString::FileSystemRepresentation (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str); + if (max_length > 0) + { + str.resize(max_length); + if (!str.empty()) + { + if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size())) + { + str.erase(::strlen(str.c_str())); + return str.c_str(); + } + } + } + } + str.erase(); + return NULL; +} + + +CFIndex +CFCString::GetLength() const +{ + CFStringRef str = get(); + if (str) + return CFStringGetLength (str); + return 0; +} diff --git a/lldb/source/Host/macosx/cfcpp/CFCString.h b/lldb/source/Host/macosx/cfcpp/CFCString.h new file mode 100644 index 000000000000..521d2c0a51b7 --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CFCString.h @@ -0,0 +1,41 @@ +//===-- CFCString.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFString_h_ +#define CoreFoundationCPP_CFString_h_ + +#include + +#include "CFCReleaser.h" + +class CFCString : public CFCReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFCString (CFStringRef cf_str = NULL); + CFCString (const char *s, CFStringEncoding encoding); + CFCString (const CFCString& rhs); + CFCString& operator= (const CFCString& rhs); + virtual ~CFCString (); + + const char * GetFileSystemRepresentation (std::string& str); + CFStringRef SetFileSystemRepresentation (const char *path); + CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type); + CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path); + const char * UTF8 (std::string& str); + CFIndex GetLength() const; + static const char *UTF8 (CFStringRef cf_str, std::string& str); + static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str); + static const char *ExpandTildeInPath(const char* path, std::string &expanded_path); + +}; + +#endif // #ifndef CoreFoundationCPP_CFString_h_ diff --git a/lldb/source/Host/macosx/cfcpp/CoreFoundationCPP.h b/lldb/source/Host/macosx/cfcpp/CoreFoundationCPP.h new file mode 100644 index 000000000000..6843e2649cda --- /dev/null +++ b/lldb/source/Host/macosx/cfcpp/CoreFoundationCPP.h @@ -0,0 +1,30 @@ +//===-- CoreFoundationCPP.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// CoreFoundationCPP.h +// CoreFoundationCPP +// +// Created by Greg Clayton on 4/23/09. +// +// +//---------------------------------------------------------------------- + +#ifndef CoreFoundationCPP_CoreFoundationCPP_H_ +#define CoreFoundationCPP_CoreFoundationCPP_H_ + +#include +#include +#include +#include +#include +#include +#include + +#endif // CoreFoundationCPP_CoreFoundationCPP_H_ diff --git a/lldb/source/Interpreter/CommandCompletions.cpp b/lldb/source/Interpreter/CommandCompletions.cpp new file mode 100644 index 000000000000..a299ffb098f1 --- /dev/null +++ b/lldb/source/Interpreter/CommandCompletions.cpp @@ -0,0 +1,414 @@ +//===-- CommandCompletions.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/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandCompletions.h" + + +using namespace lldb_private; + +CommandCompletions::CommonCompletionElement +CommandCompletions::g_common_completions[] = +{ + {eCustomCompletion, NULL}, + {eSourceFileCompletion, CommandCompletions::SourceFiles}, + {eDiskFileCompletion, NULL}, + {eSymbolCompletion, CommandCompletions::Symbols}, + {eModuleCompletion, CommandCompletions::Modules}, + {eNoCompletion, NULL} // This one has to be last in the list. +}; + +bool +CommandCompletions::InvokeCommonCompletionCallbacks (uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::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 (completion_str, + match_start_point, + max_return_elements, + interpreter, + searcher, + matches); + } + } + return handled; +} + +int +CommandCompletions::SourceFiles (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + // Find some way to switch "include support files..." + SourceFileCompleter completer (false, partial_file_name, match_start_point, max_return_elements, interpreter, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::Modules (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + ModuleCompleter completer(partial_file_name, match_start_point, max_return_elements, interpreter, matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::Symbols (const char *partial_file_name, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + SearchFilter *searcher, + lldb_private::StringList &matches) +{ + SymbolCompleter completer(partial_file_name, match_start_point, max_return_elements, interpreter, matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +CommandCompletions::Completer::Completer ( + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + m_completion_str (completion_str), + m_match_start_point (match_start_point), + m_max_return_elements (max_return_elements), + m_interpreter (interpreter), + m_matches (matches) +{ +} + +CommandCompletions::Completer::~Completer () +{ + +} + +//---------------------------------------------------------------------- +// SourceFileCompleter +//---------------------------------------------------------------------- + +CommandCompletions::SourceFileCompleter::SourceFileCompleter ( + bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + CommandCompletions::Completer (completion_str, match_start_point, max_return_elements, interpreter, matches), + m_include_support_files (include_support_files), + m_matching_files() +{ + FileSpec partial_spec (m_completion_str.c_str()); + 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 == ')') + return true; + else + return false; +} +CommandCompletions::SymbolCompleter::SymbolCompleter ( + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + CommandCompletions::Completer (completion_str, match_start_point, max_return_elements, interpreter, matches) +{ + std::string regex_str ("^"); + regex_str.append(completion_str); + regex_str.append(".*"); + std::string::iterator pos; + + pos = find_if(regex_str.begin(), regex_str.end(), regex_chars); + while (pos < regex_str.end()) { + pos = regex_str.insert(pos, '\\'); + pos += 2; + pos = find_if(pos, 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 +) +{ + SymbolContextList func_list; + SymbolContextList sym_list; + + if (context.module_sp != NULL) + { + if (context.module_sp) + { + context.module_sp->FindSymbolsMatchingRegExAndType (m_regex, lldb::eSymbolTypeCode, sym_list); + context.module_sp->FindFunctions (m_regex, true, func_list); + } + + SymbolContext sc; + // Now add the functions & symbols to the list - only add if unique: + for (int i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc)) + { + if (sc.function) + { + m_match_set.insert (sc.function->GetMangled().GetDemangledName()); + } + } + } + + for (int i = 0; i < sym_list.GetSize(); i++) + { + if (sym_list.GetContextAtIndex(i, sc)) + { + if (sc.symbol && sc.symbol->GetAddressRangePtr()) + { + m_match_set.insert (sc.symbol->GetMangled().GetDemangledName()); + } + } + } + } + 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 ( + const char *completion_str, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) : + CommandCompletions::Completer (completion_str, match_start_point, max_return_elements, interpreter, matches) +{ + FileSpec partial_spec (m_completion_str.c_str()); + 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 != NULL) + { + 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/lldb/source/Interpreter/CommandContext.cpp b/lldb/source/Interpreter/CommandContext.cpp new file mode 100644 index 000000000000..012611c52624 --- /dev/null +++ b/lldb/source/Interpreter/CommandContext.cpp @@ -0,0 +1,77 @@ +//===-- CommandContext.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/CommandContext.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +CommandContext::CommandContext () : + m_exe_ctx () +{ +} + +CommandContext::~CommandContext () +{ +} + +Target * +CommandContext::GetTarget() +{ + return Debugger::GetSharedInstance().GetCurrentTarget().get(); +} + + +ExecutionContext & +CommandContext::GetExecutionContext() +{ + return m_exe_ctx; +} + +void +CommandContext::Update (ExecutionContext *override_context) +{ + m_exe_ctx.Clear(); + + if (override_context != NULL) + { + m_exe_ctx.target = override_context->target; + m_exe_ctx.process = override_context->process; + m_exe_ctx.thread = override_context->thread; + m_exe_ctx.frame = override_context->frame; + } + else + { + TargetSP target_sp (Debugger::GetSharedInstance().GetCurrentTarget()); + if (target_sp) + { + m_exe_ctx.process = target_sp->GetProcessSP().get(); + if (m_exe_ctx.process && m_exe_ctx.process->IsRunning() == false) + { + m_exe_ctx.thread = m_exe_ctx.process->GetThreadList().GetCurrentThread().get(); + if (m_exe_ctx.thread == NULL) + m_exe_ctx.thread = m_exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (m_exe_ctx.thread) + { + m_exe_ctx.frame = m_exe_ctx.thread->GetCurrentFrame().get(); + if (m_exe_ctx.frame == NULL) + m_exe_ctx.frame = m_exe_ctx.thread->GetStackFrameAtIndex (0).get(); + } + } + } + } +} diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp new file mode 100644 index 000000000000..ed85b33bab30 --- /dev/null +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -0,0 +1,1300 @@ +//===-- 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 + +#include +#include + +#include "CommandObjectAdd.h" +#include "CommandObjectAlias.h" +#include "CommandObjectAppend.h" +#include "CommandObjectApropos.h" +#include "CommandObjectArgs.h" +#include "CommandObjectBreakpoint.h" +#include "CommandObjectCall.h" +#include "CommandObjectDelete.h" +#include "CommandObjectDisassemble.h" +#include "CommandObjectExpression.h" +#include "CommandObjectFile.h" +#include "CommandObjectFrame.h" +#include "CommandObjectHelp.h" +#include "CommandObjectImage.h" +#include "CommandObjectInfo.h" +#include "CommandObjectLog.h" +#include "CommandObjectMemory.h" +#include "CommandObjectProcess.h" +#include "CommandObjectQuit.h" +#include "CommandObjectRegexCommand.h" +#include "CommandObjectRegister.h" +#include "CommandObjectRemove.h" +#include "CommandObjectScript.h" +#include "CommandObjectSelect.h" +#include "CommandObjectSet.h" +#include "CommandObjectSettings.h" +#include "CommandObjectShow.h" +#include "CommandObjectSource.h" +#include "CommandObjectSourceFile.h" +#include "CommandObjectStatus.h" +#include "CommandObjectSyntax.h" +#include "CommandObjectTarget.h" +#include "CommandObjectThread.h" +#include "CommandObjectTranslate.h" +#include "CommandObjectUnalias.h" +#include "CommandObjectVariable.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/TargetList.h" + +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +CommandInterpreter::CommandInterpreter +( + ScriptLanguage script_language, + bool synchronous_execution, + Listener *listener, + SourceManager& source_manager +) : + Broadcaster ("CommandInterpreter"), + m_script_language (script_language), + m_synchronous_execution (synchronous_execution), + m_listener (listener), + m_source_manager (source_manager) +{ +} + +void +CommandInterpreter::Initialize () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + CommandReturnObject result; + + LoadCommandDictionary (); + + InitializeVariables (); + + // Set up some initial aliases. + result.Clear(); HandleCommand ("alias q quit", false, result); + result.Clear(); HandleCommand ("alias run process launch", false, result); + result.Clear(); HandleCommand ("alias r process launch", false, result); + result.Clear(); HandleCommand ("alias c process continue", false, result); + result.Clear(); HandleCommand ("alias continue process continue", false, result); + result.Clear(); HandleCommand ("alias expr expression", false, result); + result.Clear(); HandleCommand ("alias exit quit", false, result); + result.Clear(); HandleCommand ("alias bt thread backtrace", false, result); + result.Clear(); HandleCommand ("alias si thread step-inst", false, result); + result.Clear(); HandleCommand ("alias step thread step-in", false, result); + result.Clear(); HandleCommand ("alias s thread step-in", false, result); + result.Clear(); HandleCommand ("alias next thread step-over", false, result); + result.Clear(); HandleCommand ("alias n thread step-over", false, result); + result.Clear(); HandleCommand ("alias finish thread step-out", false, result); + result.Clear(); HandleCommand ("alias x memory read", false, result); + result.Clear(); HandleCommand ("alias l source-file", false, result); + result.Clear(); HandleCommand ("alias list source-file", false, result); +} + +void +CommandInterpreter::InitializeVariables () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + m_variables["prompt"] = + StateVariableSP (new StateVariable ("prompt", + "(lldb) ", + false, + "The debugger prompt displayed for the user.", + StateVariable::BroadcastPromptChange)); + + m_variables["run-args"] = + StateVariableSP (new StateVariable ("run-args", + (Args*)NULL, + "An argument list containing the arguments to be passed to the executable when it is launched.")); + + + m_variables["env-vars"] = + StateVariableSP (new StateVariable ("env-vars", + (Args*)NULL, + "A list of strings containing the environment variables to be passed to the executable's environment.")); + + m_variables["input-path"] = + StateVariableSP (new StateVariable ("input-path", + "/dev/stdin", + false, + "The file/path to be used by the executable program for reading its input.")); + + m_variables["output-path"] = + StateVariableSP (new StateVariable ( "output-path", + "/dev/stdout", + false, + "The file/path to be used by the executable program for writing its output.")); + + m_variables["error-path"] = + StateVariableSP (new StateVariable ("error-path", + "/dev/stderr", + false, + "The file/path to be used by the executable program for writing its error messages.")); + + m_variables["arch"] = + StateVariableSP (new StateVariable ("arch", + "", + false, + "The architecture to be used for running the executable (e.g. i386, x86_64, etc).")); + + m_variables["script-lang"] = + StateVariableSP (new StateVariable ("script-lang", + "Python", + false, + "The script language to be used for evaluating user-written scripts.", + StateVariable::VerifyScriptLanguage)); + + m_variables["term-width"] = + StateVariableSP (new StateVariable ("term-width", + 80, + "The maximum number of columns to use for displaying text.")); + +} + +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__); + + // **** IMPORTANT **** IMPORTANT *** IMPORTANT *** **** IMPORTANT **** IMPORTANT *** IMPORTANT *** + // + // Command objects that are used as cross reference objects (i.e. they inherit from CommandObjectCrossref) + // *MUST* be created and put into the command dictionary *BEFORE* any multi-word commands (which may use + // the cross-referencing stuff) are created!!! + // + // **** IMPORTANT **** IMPORTANT *** IMPORTANT *** **** IMPORTANT **** IMPORTANT *** IMPORTANT *** + + + // Command objects that inherit from CommandObjectCrossref must be created before other command objects + // are created. This is so that when another command is created that needs to go into a crossref object, + // the crossref object exists and is ready to take the cross reference. Put the cross referencing command + // objects into the CommandDictionary now, so they are ready for use when the other commands get created. + + m_command_dict["select"] = CommandObjectSP (new CommandObjectSelect ()); + m_command_dict["info"] = CommandObjectSP (new CommandObjectInfo ()); + m_command_dict["delete"] = CommandObjectSP (new CommandObjectDelete ()); + + // Non-CommandObjectCrossref commands can now be created. + + //m_command_dict["add"] = CommandObjectSP (new CommandObjectAdd ()); + m_command_dict["alias"] = CommandObjectSP (new CommandObjectAlias ()); + m_command_dict["append"] = CommandObjectSP (new CommandObjectAppend ()); + m_command_dict["apropos"] = CommandObjectSP (new CommandObjectApropos ()); + //m_command_dict["args"] = CommandObjectSP (new CommandObjectArgs ()); + m_command_dict["breakpoint"]= CommandObjectSP (new CommandObjectMultiwordBreakpoint (this)); + m_command_dict["call"] = CommandObjectSP (new CommandObjectCall ()); + m_command_dict["disassemble"] = CommandObjectSP (new CommandObjectDisassemble ()); + m_command_dict["expression"]= CommandObjectSP (new CommandObjectExpression ()); + m_command_dict["file"] = CommandObjectSP (new CommandObjectFile ()); + m_command_dict["frame"] = CommandObjectSP (new CommandObjectMultiwordFrame (this)); + m_command_dict["help"] = CommandObjectSP (new CommandObjectHelp ()); + m_command_dict["image"] = CommandObjectSP (new CommandObjectImage (this)); + m_command_dict["log"] = CommandObjectSP (new CommandObjectLog (this)); + m_command_dict["memory"] = CommandObjectSP (new CommandObjectMemory (this)); + m_command_dict["process"] = CommandObjectSP (new CommandObjectMultiwordProcess (this)); + m_command_dict["quit"] = CommandObjectSP (new CommandObjectQuit ()); + m_command_dict["register"] = CommandObjectSP (new CommandObjectRegister (this)); + //m_command_dict["remove"] = CommandObjectSP (new CommandObjectRemove ()); + m_command_dict["script"] = CommandObjectSP (new CommandObjectScript (m_script_language)); + m_command_dict["set"] = CommandObjectSP (new CommandObjectSet ()); + m_command_dict["settings"] = CommandObjectSP (new CommandObjectSettings ()); + m_command_dict["show"] = CommandObjectSP (new CommandObjectShow ()); + m_command_dict["source"] = CommandObjectSP (new CommandObjectSource ()); + m_command_dict["source-file"] = CommandObjectSP (new CommandObjectSourceFile ()); + //m_command_dict["syntax"] = CommandObjectSP (new CommandObjectSyntax ()); + m_command_dict["status"] = CommandObjectSP (new CommandObjectStatus ()); + m_command_dict["target"] = CommandObjectSP (new CommandObjectMultiwordTarget (this)); + m_command_dict["thread"] = CommandObjectSP (new CommandObjectMultiwordThread (this)); + //m_command_dict["translate"] = CommandObjectSP (new CommandObjectTranslate ()); + m_command_dict["unalias"] = CommandObjectSP (new CommandObjectUnalias ()); + m_command_dict["variable"] = CommandObjectSP (new CommandObjectVariable (this)); + + std::auto_ptr + break_regex_cmd_ap(new CommandObjectRegexCommand ("regexp-break", + "Smart breakpoint command (using regular expressions).", + "regexp-break [:]\nregexp-break [
]\nregexp-break <...>", 2)); + if (break_regex_cmd_ap.get()) + { + if (break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2") && + break_regex_cmd_ap->AddRegexCommand("^(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1") && + break_regex_cmd_ap->AddRegexCommand("^[\"']?([-+]\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'") && + break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list") && + break_regex_cmd_ap->AddRegexCommand("^(-.*)$", "breakpoint set %1") && + break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1'")) + { + CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release()); + m_command_dict[break_regex_cmd_sp->GetCommandName ()] = break_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 ret_val; + + std::string cmd(cmd_cstr); + + if (HasCommands()) + { + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + ret_val = pos->second; + } + + if (include_aliases && HasAliases()) + { + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + ret_val = pos->second; + } + + if (HasUserCommands()) + { + pos = m_user_dict.find(cmd); + if (pos != m_user_dict.end()) + ret_val = pos->second; + } + + if (!exact && ret_val == NULL) + { + StringList local_matches; + if (matches == NULL) + matches = &local_matches; + + int num_cmd_matches = 0; + int num_alias_matches = 0; + int num_user_matches = 0; + 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()) + ret_val = pos->second; + } + + if (num_cmd_matches != 1 && 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()) + { + matches->Clear(); + matches->AppendString (cmd.c_str()); + + ret_val = pos->second; + } + } + + if (num_cmd_matches != 1 && num_alias_matches != 1 && 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()) + { + matches->Clear(); + matches->AppendString (cmd.c_str()); + + ret_val = pos->second; + } + } + } + else { + if (matches) + matches->AppendString (cmd_cstr); + } + + + return ret_val; +} + +CommandObject * +CommandInterpreter::GetCommandObject (const char *cmd_cstr, bool include_aliases, bool exact, StringList *matches) +{ + return GetCommandSP (cmd_cstr, include_aliases, exact, matches).get(); +} + +bool +CommandInterpreter::CommandExists (const char *cmd) +{ + return m_command_dict.find(cmd) != m_command_dict.end(); +} + +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) +{ + 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; +} + +StateVariable * +CommandInterpreter::GetStateVariable(const char *name) +{ + VariableMap::const_iterator pos = m_variables.find(name); + if (pos != m_variables.end()) + return pos->second.get(); + return NULL; +} + +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 != NULL) + { + OptionArgVector *options = option_arg_vector_sp.get(); + for (int i = 0; i < options->size(); ++i) + { + OptionArgPair cur_option = (*options)[i]; + std::string opt = cur_option.first; + std::string value = cur_option.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.c_str()) > max_len)) + { + longest_word = pos->first; + max_len = strlen (longest_word.c_str()); + } + } + + return longest_word; +} + +void +CommandInterpreter::GetHelp (CommandReturnObject &result) +{ + CommandObject::CommandMap::const_iterator pos; + result.AppendMessage("The following is a list of built-in, permanent debugger commands:"); + result.AppendMessage(""); + std::string longest_word = FindLongestCommandWord (m_command_dict); + uint32_t max_len = strlen (longest_word.c_str()); + + 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.size() > 0) + { + result.AppendMessage("The following is a list of your current command abbreviations (see 'alias' for more info):"); + result.AppendMessage(""); + longest_word = FindLongestCommandWord (m_alias_dict); + max_len = strlen (longest_word.c_str()); + 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.size() > 0) + { + result.AppendMessage ("The following is a list of your current user-defined commands:"); + result.AppendMessage(""); + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) + { + result.AppendMessageWithFormat ("%s -- %s\n", pos->first.c_str(), pos->second->GetHelp()); + } + result.AppendMessage(""); + } + + result.AppendMessage("For more information on any particular command, try 'help '."); +} + +void +CommandInterpreter::ShowVariableValues (CommandReturnObject &result) +{ + result.AppendMessage ("Below is a list of all the debugger setting variables and their values:"); + + for (VariableMap::const_iterator pos = m_variables.begin(); pos != m_variables.end(); ++pos) + { + StateVariable *var = pos->second.get(); + var->AppendVariableInformation (result); + } +} + +void +CommandInterpreter::ShowVariableHelp (CommandReturnObject &result) +{ + result.AppendMessage ("Below is a list of all the internal debugger variables that are settable:"); + for (VariableMap::const_iterator pos = m_variables.begin(); pos != m_variables.end(); ++pos) + { + StateVariable *var = pos->second.get(); + result.AppendMessageWithFormat (" %s -- %s \n", var->GetName(), var->GetHelp()); + } +} + +// Main entry point into the command_interpreter; this function takes a text +// line containing a debugger command, with all its flags, options, etc, +// parses the line and takes the appropriate actions. + +bool +CommandInterpreter::HandleCommand (const char *command_line, bool add_to_history, CommandReturnObject &result, + ExecutionContext *override_context) +{ + // FIXME: there should probably be a mutex to make sure only one thread can + // run the interpreter at a time. + + // TODO: this should be a logging channel in lldb. +// if (DebugSelf()) +// { +// result.AppendMessageWithFormat ("Processing command: %s\n", command_line); +// } + + m_current_context.Update (override_context); + + if (command_line == NULL || command_line[0] == '\0') + { + if (m_command_history.empty()) + { + result.AppendError ("empty command"); + result.SetStatus(eReturnStatusFailed); + return false; + } + else + { + command_line = m_command_history.back().c_str(); + } + add_to_history = false; + } + + Args command_args(command_line); + + if (command_args.GetArgumentCount() > 0) + { + const char *command_cstr = command_args.GetArgumentAtIndex(0); + if (command_cstr) + { + + // We're looking up the command object here. So first find an exact match to the + // command in the commands. + + CommandObject *command_obj = GetCommandObject (command_cstr, false, true); + + // If we didn't find an exact match to the command string in the commands, look in + // the aliases. + + if (command_obj == NULL) + { + command_obj = GetCommandObject (command_cstr, true, true); + if (command_obj != NULL) + { + BuildAliasCommandArgs (command_obj, command_cstr, command_args, result); + if (!result.Succeeded()) + return false; + } + } + + // Finally, if there wasn't an exact match among the aliases, look for an inexact match. + + if (command_obj == NULL) + command_obj = GetCommandObject(command_cstr, false, false); + + if (command_obj) + { + if (command_obj->WantsRawCommandString()) + { + const char *stripped_command = ::strstr (command_line, command_cstr); + if (stripped_command) + { + stripped_command += strlen(command_cstr); + while (isspace(*stripped_command)) + ++stripped_command; + command_obj->ExecuteRawCommandString(stripped_command, Context(), this, result); + } + } + else + { + if (add_to_history) + m_command_history.push_back (command_line); + + // Remove the command from the args. + command_args.Shift(); + command_obj->ExecuteWithOptions (command_args, Context(), this, result); + } + } + else + { + StringList matches; + int num_matches; + int cursor_index = command_args.GetArgumentCount() - 1; + int cursor_char_position = strlen (command_args.GetArgumentAtIndex(command_args.GetArgumentCount() - 1)); + num_matches = HandleCompletionMatches (command_args, cursor_index, + cursor_char_position, + 0, -1, matches); + + if (num_matches > 0) + { + std::string error_msg; + error_msg.assign ("ambiguous command '"); + error_msg.append(command_cstr); + 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(), error_msg.size()); + } + else + result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_cstr); + + result.SetStatus (eReturnStatusFailed); + } + } + } + return result.Succeeded(); +} + +int +CommandInterpreter::HandleCompletionMatches (Args &parsed_line, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + StringList &matches) +{ + int num_command_matches = 0; + bool include_aliases = true; + bool look_for_subcommand = false; + + if (cursor_index == -1) + { + // We got nothing on the command line, so return the list of commands + 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), include_aliases, false, + &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), include_aliases, false); + 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, this, + 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); + + 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)); + + int num_command_matches; + + matches.Clear(); + + // Only max_return_elements == -1 is supported at present: + assert (max_return_elements == -1); + num_command_matches = HandleCompletionMatches (parsed_line, cursor_index, cursor_char_position, match_start_point, + max_return_elements, 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); + int partial_name_len = command_partial_str.size(); + + // If we matched a unique single command, add a space... + if (num_command_matches == 1) + { + 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; +} + +CommandContext * +CommandInterpreter::Context () +{ + return &m_current_context; +} + +const Args * +CommandInterpreter::GetProgramArguments () +{ + if (! HasInterpreterVariables()) + return NULL; + + VariableMap::const_iterator pos = m_variables.find("run-args"); + if (pos == m_variables.end()) + return NULL; + + StateVariable *var = pos->second.get(); + + if (var) + return &var->GetArgs(); + return NULL; +} + +const Args * +CommandInterpreter::GetEnvironmentVariables () +{ + if (! HasInterpreterVariables()) + return NULL; + + VariableMap::const_iterator pos = m_variables.find("env-vars"); + if (pos == m_variables.end()) + return NULL; + + StateVariable *var = pos->second.get(); + if (var) + return &var->GetArgs(); + return NULL; +} + + +CommandInterpreter::~CommandInterpreter () +{ +} + +const char * +CommandInterpreter::GetPrompt () +{ + VariableMap::iterator pos; + + if (! HasInterpreterVariables()) + return NULL; + + pos = m_variables.find("prompt"); + if (pos == m_variables.end()) + return NULL; + + StateVariable *var = pos->second.get(); + + return ((char *) var->GetStringValue()); +} + +void +CommandInterpreter::SetPrompt (const char *new_prompt) +{ + VariableMap::iterator pos; + CommandReturnObject result; + + if (! HasInterpreterVariables()) + return; + + pos = m_variables.find ("prompt"); + if (pos == m_variables.end()) + return; + + StateVariable *var = pos->second.get(); + + if (var->VerifyValue (this, (void *) new_prompt, result)) + var->SetStringValue (new_prompt); +} + +void +CommandInterpreter::CrossRegisterCommand (const char * dest_cmd, const char * object_type) +{ + CommandObjectSP cmd_obj_sp = GetCommandSP (dest_cmd); + + if (cmd_obj_sp != NULL) + { + CommandObject *cmd_obj = cmd_obj_sp.get(); + if (cmd_obj->IsCrossRefObject ()) + cmd_obj->AddObject (object_type); + } +} + +void +CommandInterpreter::SetScriptLanguage (ScriptLanguage lang) +{ + m_script_language = lang; +} + +Listener * +CommandInterpreter::GetListener () +{ + return m_listener; +} + +SourceManager & +CommandInterpreter::GetSourceManager () +{ + return m_source_manager; +} + + + +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()); +} + +bool +CommandInterpreter::HasInterpreterVariables () +{ + return (!m_variables.empty()); +} + +void +CommandInterpreter::BuildAliasCommandArgs +( + CommandObject *alias_cmd_obj, + const char *alias_name, + Args &cmd_args, + CommandReturnObject &result +) +{ + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp.get()) + { + // 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(); + + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + int old_size = cmd_args.GetArgumentCount(); + int *used = (int *) malloc ((old_size + 1) * sizeof (int)); + + memset (used, 0, (old_size + 1) * sizeof (int)); + used[0] = 1; + + for (int i = 0; i < option_arg_vector->size(); ++i) + { + OptionArgPair option_pair = (*option_arg_vector)[i]; + std::string option = option_pair.first; + std::string value = option_pair.second; + if (option.compare ("") == 0) + new_args.AppendArgument (value.c_str()); + else + { + 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 + new_args.AppendArgument (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; + } + else + { + new_args.AppendArgument (cmd_args.GetArgumentAtIndex (index)); + used[index] = 1; + } + } + } + } + + for (int j = 0; j < cmd_args.GetArgumentCount(); ++j) + { + if (!used[j]) + 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. + 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) +{ + const char *init_file_path = in_cwd ? "./.lldbinit" : "~/.lldbinit"; + FileSpec init_file (init_file_path); + // 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()) + { + char path[PATH_MAX]; + init_file.GetPath(path, sizeof(path)); + StreamString source_command; + source_command.Printf ("source '%s'", path); + HandleCommand (source_command.GetData(), false, result); + } + else + { + // nothing to be done if the file doesn't exist + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } +} + +ScriptInterpreter * +CommandInterpreter::GetScriptInterpreter () +{ + CommandObject::CommandMap::iterator pos; + + pos = m_command_dict.find ("script"); + if (pos != m_command_dict.end()) + { + CommandObject *script_cmd_obj = pos->second.get(); + return ((CommandObjectScript *) script_cmd_obj)->GetInterpreter (); + } + else + return NULL; +} + + + +bool +CommandInterpreter::GetSynchronous () +{ + return m_synchronous_execution; +} + +void +CommandInterpreter::SetSynchronous (bool value) +{ + static bool value_set_once = false; + if (!value_set_once) + { + value_set_once = true; + m_synchronous_execution = value; + } +} + +void +CommandInterpreter::OutputFormattedHelpText (Stream &strm, + const char *word_text, + const char *separator, + const char *help_text, + uint32_t max_word_len) +{ + StateVariable *var = GetStateVariable ("term-width"); + int max_columns = var->GetIntValue(); + // Sanity check max_columns, to cope with emacs shell mode with TERM=dumb + // (0 rows; 0 columns;). + if (max_columns <= 0) max_columns = 80; + + int indent_size = max_word_len + strlen (separator) + 2; + + strm.IndentMore (indent_size); + + int len = indent_size + strlen (help_text) + 1; + char *text = (char *) malloc (len); + sprintf (text, "%-*s %s %s", max_word_len, word_text, separator, help_text); + if (text[len - 1] == '\n') + text[--len] = '\0'; + + 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; + int start = 0; + int end = start; + int final_end = strlen (text); + int sub_len; + + 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--; + } + + 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); + free (text); +} + +void +CommandInterpreter::AproposAllSubCommands (CommandObject *cmd_obj, const char *prefix, const char *search_word, + StringList &commands_found, StringList &commands_help) +{ + CommandObject::CommandMap::const_iterator pos; + CommandObject::CommandMap sub_cmd_dict = ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict; + CommandObject *sub_cmd_obj; + + for (pos = sub_cmd_dict.begin(); pos != sub_cmd_dict.end(); ++pos) + { + const char * command_name = pos->first.c_str(); + 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()) + AproposAllSubCommands (sub_cmd_obj, complete_command_name.GetData(), search_word, commands_found, + commands_help); + } + +} + +void +CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList &commands_found, + StringList &commands_help) +{ + CommandObject::CommandMap::const_iterator pos; + + 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()) + AproposAllSubCommands (cmd_obj, command_name, search_word, commands_found, commands_help); + + } +} diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp new file mode 100644 index 000000000000..080b5b057bf6 --- /dev/null +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -0,0 +1,448 @@ +//===-- 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/Interpreter/CommandObject.h" + +#include +#include + +#include +#include +#include + +#include "lldb/Core/Address.h" +#include "lldb/Core/Options.h" + +// These are for the Sourcename completers. +// FIXME: Make a separate file for the completers. +#include "lldb/Core/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 (const char *name, const char *help, const char *syntax, uint32_t flags) : + m_cmd_name (name), + m_cmd_help_short (), + m_cmd_help_long (), + m_cmd_syntax (), + m_flags (flags) +{ + 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 () +{ + return m_cmd_syntax.c_str(); +} + +const char * +CommandObject::Translate () +{ + //return m_cmd_func_name.c_str(); + return "This function is currently not implemented."; +} + +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::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; +} + +Flags& +CommandObject::GetFlags() +{ + return m_flags; +} + +const Flags& +CommandObject::GetFlags() const +{ + return m_flags; +} + +bool +CommandObject::ExecuteCommandString +( + const char *command_line, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + Args command_args(command_line); + return ExecuteWithOptions (command_args, context, interpreter, result); +} + +bool +CommandObject::ParseOptions +( + Args& args, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + // See if the subclass has options? + Options *options = GetOptions(); + if (options != NULL) + { + Error error; + options->ResetOptionValues(); + + // ParseOptions calls getopt_long, 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.Fail() || !options->VerifyOptions (result)) + { + const char *error_cstr = error.AsCString(); + if (error_cstr) + { + // We got an error string, lets use that + result.GetErrorStream().PutCString(error_cstr); + } + else + { + // No error string, output the usage information into result + options->GenerateOptionUsage (result.GetErrorStream(), this); + } + // Set the return status to failed (this was an error). + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return true; +} +bool +CommandObject::ExecuteWithOptions +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + for (size_t i = 0; i < args.GetArgumentCount(); ++i) + { + const char *tmp_str = args.GetArgumentAtIndex (i); + if (tmp_str[0] == '`') // back-quote + args.ReplaceArgumentAtIndex (i, interpreter->ProcessEmbeddedScriptCommands (tmp_str)); + } + + Process *process = context->GetExecutionContext().process; + if (process == NULL) + { + if (GetFlags().IsSet(CommandObject::eFlagProcessMustBeLaunched | CommandObject::eFlagProcessMustBePaused)) + { + result.AppendError ("Process must exist."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + StateType state = process->GetState(); + + switch (state) + { + + case eStateAttaching: + case eStateLaunching: + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + break; + + case eStateDetached: + case eStateExited: + case eStateUnloaded: + if (GetFlags().IsSet(CommandObject::eFlagProcessMustBeLaunched)) + { + result.AppendError ("Process must be launched."); + result.SetStatus (eReturnStatusFailed); + return false; + } + break; + + case eStateRunning: + case eStateStepping: + if (GetFlags().IsSet(CommandObject::eFlagProcessMustBePaused)) + { + result.AppendError ("Process is running. Use 'process interrupt' to pause execution."); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (!ParseOptions (args, interpreter, result)) + return false; + + // Call the command-specific version of 'Execute', passing it the already processed arguments. + return Execute (args, context, interpreter, result); +} + +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 1; + + size_t found = map_element.first.find (m_match_str, 0); + if (found == std::string::npos) + return 0; + else + return found == 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, + CommandInterpreter *interpreter, + StringList &matches +) +{ + if (WantsRawCommandString()) + { + // 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 will freak out. + + input.AppendArgument (""); + + input.ParseArgsForCompletion (*cur_options, opt_element_vector); + + 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, + interpreter, + 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, + interpreter, + matches); + } +} + +int +CommandObject::HandleArgumentCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) +{ + return 0; +} + +// Case insensitive version of ::strstr() +// Returns true if s2 is contained within s1. + +static bool +contains_string (const char *s1, const char *s2) +{ + char *locase_s1 = (char *) malloc (strlen (s1) + 1); + char *locase_s2 = (char *) malloc (strlen (s2) + 1); + int i; + for (i = 0; s1 && s1[i] != '\0'; i++) + locase_s1[i] = ::tolower (s1[i]); + locase_s1[i] = '\0'; + for (i = 0; s2 && s2[i] != '\0'; i++) + locase_s2[i] = ::tolower (s2[i]); + locase_s2[i] = '\0'; + + const char *result = ::strstr (locase_s1, locase_s2); + free (locase_s1); + free (locase_s2); + // 'result' points into freed memory - but we're not + // deref'ing it so hopefully current/future compilers + // won't complain.. + + if (result == NULL) + return false; + else + return true; +} + +bool +CommandObject::HelpTextContainsWord (const char *search_word) +{ + const char *short_help; + const char *long_help; + const char *syntax_help; + std::string options_usage_help; + + + bool found_word = false; + + short_help = GetHelp(); + long_help = GetHelpLong(); + syntax_help = GetSyntax(); + + if (contains_string (short_help, search_word)) + found_word = true; + else if (contains_string (long_help, search_word)) + found_word = true; + else if (contains_string (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 (contains_string (usage_text, search_word)) + found_word = true; + } + } + + return found_word; +} diff --git a/lldb/source/Interpreter/CommandObjectCrossref.cpp b/lldb/source/Interpreter/CommandObjectCrossref.cpp new file mode 100644 index 000000000000..27b66379e874 --- /dev/null +++ b/lldb/source/Interpreter/CommandObjectCrossref.cpp @@ -0,0 +1,92 @@ +//===-- CommandObjectCrossref.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/CommandObjectCrossref.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectCrossref +//------------------------------------------------------------------------- + +CommandObjectCrossref::CommandObjectCrossref +( + const char *name, + const char *help, + const char *syntax +) : + CommandObject (name, help, syntax), + m_crossref_object_types() +{ +} + +CommandObjectCrossref::~CommandObjectCrossref () +{ +} + +bool +CommandObjectCrossref::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + if (m_crossref_object_types.GetArgumentCount() == 0) + { + result.AppendErrorWithFormat ("There are no objects for which you can call '%s'.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + else + { + GenerateHelpText (result); + } + return result.Succeeded(); +} + +void +CommandObjectCrossref::AddObject (const char *obj_name) +{ + m_crossref_object_types.AppendArgument (obj_name); +} + +const char ** +CommandObjectCrossref::GetObjectTypes () const +{ + return m_crossref_object_types.GetConstArgumentVector(); +} + +void +CommandObjectCrossref::GenerateHelpText (CommandReturnObject &result) +{ + result.AppendMessage ("This command can be called on the following types of objects:"); + + for (int i = 0; i < m_crossref_object_types.GetArgumentCount(); ++i) + { + const char *obj_name = m_crossref_object_types.GetArgumentAtIndex(i); + result.AppendMessageWithFormat (" %s (e.g. '%s %s')\n", obj_name, + obj_name, GetCommandName()); + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + +bool +CommandObjectCrossref::IsCrossRefObject () +{ + return true; +} diff --git a/lldb/source/Interpreter/CommandObjectMultiword.cpp b/lldb/source/Interpreter/CommandObjectMultiword.cpp new file mode 100644 index 000000000000..874be0ea6a1d --- /dev/null +++ b/lldb/source/Interpreter/CommandObjectMultiword.cpp @@ -0,0 +1,263 @@ +//===-- 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/Interpreter/CommandObjectMultiword.h" +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandContext.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectMultiword +//------------------------------------------------------------------------- + +CommandObjectMultiword::CommandObjectMultiword +( + const char *name, + const char *help, + const char *syntax, + uint32_t flags +) : + CommandObject (name, help, syntax, flags) +{ +} + +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()) + 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 (CommandObjectSP cmd_obj, const char *name, + CommandInterpreter *interpreter) +{ + CommandMap::iterator pos; + bool success = true; + + pos = m_subcommand_dict.find(name); + if (pos == m_subcommand_dict.end()) + { + m_subcommand_dict[name] = cmd_obj; + interpreter->CrossRegisterCommand (name, GetCommandName()); + } + else + success = false; + + return success; +} + +bool +CommandObjectMultiword::Execute +( + Args& args, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + GenerateHelpText (result, interpreter); + } + else + { + const char *sub_command = args.GetArgumentAtIndex (0); + + if (sub_command) + { + if (::strcasecmp (sub_command, "help") == 0) + { + GenerateHelpText (result, interpreter); + } + 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->ExecuteWithOptions (args, context, interpreter, result); + } + else + { + std::string error_msg; + int 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 (int 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(), error_msg.size()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("'%s' does not have any subcommands.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} + +void +CommandObjectMultiword::GenerateHelpText (CommandReturnObject &result, CommandInterpreter *interpreter) +{ + // First time through here, generate the help text for the object and + // push it to the return result object as well + + StreamString &output_stream = result.GetOutputStream(); + output_stream.PutCString ("The following subcommands are supported:\n\n"); + + CommandMap::iterator pos; + std::string longest_word = interpreter->FindLongestCommandWord (m_subcommand_dict); + uint32_t max_len = 0; + + if (! longest_word.empty()) + max_len = strlen (longest_word.c_str()) + 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); + interpreter->OutputFormattedHelpText (result.GetOutputStream(), indented_command.c_str(), "--", + pos->second->GetHelp(), max_len); + } + + output_stream.PutCString ("\nFor more help on any particular subcommand, type 'help '.\n"); + + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + +int +CommandObjectMultiword::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + CommandInterpreter *interpreter, + StringList &matches +) +{ + 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, interpreter, 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, interpreter, matches); + } + + } +} + diff --git a/lldb/source/Interpreter/CommandObjectRegexCommand.cpp b/lldb/source/Interpreter/CommandObjectRegexCommand.cpp new file mode 100644 index 000000000000..b3fa6a41d979 --- /dev/null +++ b/lldb/source/Interpreter/CommandObjectRegexCommand.cpp @@ -0,0 +1,123 @@ +//===-- 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/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 +( + const char *name, + const char *help, + const char *syntax, + uint32_t max_matches +) : + CommandObject (name, help, syntax), + m_entries(), + m_max_matches (max_matches) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::~CommandObjectRegexCommand() +{ +} + + +bool +CommandObjectRegexCommand::Execute +( + Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + return false; +} + + +bool +CommandObjectRegexCommand::ExecuteRawCommandString +( + const char *command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result +) +{ + if (command) + { + EntryCollection::const_iterator pos, end = m_entries.end(); + for (pos = m_entries.begin(); pos != end; ++pos) + { + if (pos->regex.Execute (command, m_max_matches)) + { + 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 (pos->regex.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_options.verbose) +// result.GetOutputStream().Printf("%s\n", new_command.c_str()); + return interpreter->HandleCommand(new_command.c_str(), true, result); + } + } + result.SetStatus(eReturnStatusFailed); + 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 exression 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; +} diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp new file mode 100644 index 000000000000..f634e3c7df51 --- /dev/null +++ b/lldb/source/Interpreter/CommandReturnObject.cpp @@ -0,0 +1,175 @@ +//===-- 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/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +CommandReturnObject::CommandReturnObject () : + m_output_stream (), + m_error_stream (), + m_status (eReturnStatusStarted), + m_did_change_process_state (false) +{ +} + +CommandReturnObject::~CommandReturnObject () +{ +} + +StreamString & +CommandReturnObject::GetOutputStream () +{ + return m_output_stream; +} + +StreamString & +CommandReturnObject::GetErrorStream () +{ + return m_error_stream; +} + +void +CommandReturnObject::AppendErrorWithFormat (const char *format, ...) +{ + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + m_error_stream.Printf("error: %s", sstrm.GetData()); +} + + +void +CommandReturnObject::AppendMessageWithFormat (const char *format, ...) +{ + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + m_output_stream.Printf("%s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendWarningWithFormat (const char *format, ...) +{ + va_list args; + va_start (args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end (args); + + m_error_stream.Printf("warning: %s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendMessage (const char *in_string, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_output_stream.Printf("%*.*s\n", len, len, in_string); +} + +void +CommandReturnObject::AppendWarning (const char *in_string, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf("warning: %*.*s\n", len, len, 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, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf("%*.*s", len, len, in_string); +} + +void +CommandReturnObject::AppendError (const char *in_string, int len) +{ + if (!in_string) + return; + + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf ("error: %*.*s\n", len, len, in_string); +} + +// 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, int len) +{ + if (len < 0) + len = ::strlen (in_string); + m_error_stream.Printf ("%*.*s", len, len, 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() +{ + m_output_stream.Clear(); + m_error_stream.Clear(); + m_status = eReturnStatusStarted; +} + +bool +CommandReturnObject::GetDidChangeProcessState () +{ + return m_did_change_process_state; +} + +void +CommandReturnObject::SetDidChangeProcessState (bool b) +{ + m_did_change_process_state = b; +} + diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp new file mode 100644 index 000000000000..fa3ac31e6916 --- /dev/null +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -0,0 +1,66 @@ +//===-- 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/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 "PseudoTerminal.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreter::ScriptInterpreter (ScriptLanguage script_lang) : + m_script_lang (script_lang), + m_interpreter_pty () +{ + if (m_interpreter_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, NULL, 0)) + { + const char *slave_name = m_interpreter_pty.GetSlaveName(NULL, 0); + if (slave_name) + m_pty_slave_name.assign(slave_name); + } +} + +ScriptInterpreter::~ScriptInterpreter () +{ + m_interpreter_pty.CloseMasterFileDescriptor(); +} + +const char * +ScriptInterpreter::GetScriptInterpreterPtyName () +{ + return m_pty_slave_name.c_str(); +} + +int +ScriptInterpreter::GetMasterFileDescriptor () +{ + return m_interpreter_pty.GetMasterFileDescriptor(); +} + +void +ScriptInterpreter::CollectDataForBreakpointCommandCallback +( + BreakpointOptions *bp_options, + CommandReturnObject &result +) +{ + result.SetStatus (eReturnStatusFailed); + result.AppendError ("ScriptInterpreter::GetScriptCommands(StringList &) is not implemented."); +} + + diff --git a/lldb/source/Interpreter/ScriptInterpreterNone.cpp b/lldb/source/Interpreter/ScriptInterpreterNone.cpp new file mode 100644 index 000000000000..cdc399e7d860 --- /dev/null +++ b/lldb/source/Interpreter/ScriptInterpreterNone.cpp @@ -0,0 +1,38 @@ +//===-- 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/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreterNone::ScriptInterpreterNone () : + ScriptInterpreter (eScriptLanguageNone) +{ +} + +ScriptInterpreterNone::~ScriptInterpreterNone () +{ +} + +void +ScriptInterpreterNone::ExecuteOneLine (const std::string &line, FILE *out, FILE *err) +{ + ::fprintf (err, "error: there is no embedded script interpreter in this mode.\n"); +} + +void +ScriptInterpreterNone::ExecuteInterpreterLoop (FILE *out, FILE *err) +{ + fprintf (err, "error: there is no embedded script interpreter in this mode.\n"); +} + + diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp new file mode 100644 index 000000000000..6b2e3b2e7ad1 --- /dev/null +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -0,0 +1,830 @@ +//===-- 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: + +#include + +#include "lldb/Interpreter/ScriptInterpreterPython.h" + + +#include +#include +#include +#include + +#include + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Process.h" + +extern "C" void init_lldb (void); + +using namespace lldb; +using namespace lldb_private; + +const char embedded_interpreter_string[] = +"import readline\n\ +import code\n\ +import sys\n\ +import traceback\n\ +\n\ +class SimpleREPL(code.InteractiveConsole):\n\ + def __init__(self, prompt, dict):\n\ + code.InteractiveConsole.__init__(self,dict)\n\ + self.prompt = prompt\n\ + self.loop_exit = False\n\ + self.dict = dict\n\ +\n\ + def interact(self):\n\ + try:\n\ + sys.ps1\n\ + except AttributeError:\n\ + sys.ps1 = \">>> \"\n\ + try:\n\ + sys.ps2\n\ + except AttributeError:\n\ + sys.ps2 = \"... \"\n\ +\n\ + while not self.loop_exit:\n\ + try:\n\ + self.read_py_command()\n\ + except (SystemExit, EOFError):\n\ + # EOF while in Python just breaks out to top level.\n\ + self.write('\\n')\n\ + self.loop_exit = True\n\ + break\n\ + except KeyboardInterrupt:\n\ + self.write(\"\\nKeyboardInterrupt\\n\")\n\ + self.resetbuffer()\n\ + more = 0\n\ + except:\n\ + traceback.print_exc()\n\ +\n\ + def process_input (self, in_str):\n\ + # Canonicalize the format of the input string\n\ + temp_str = in_str\n\ + temp_str.strip(' \t')\n\ + words = temp_str.split()\n\ + temp_str = ('').join(words)\n\ +\n\ + # Check the input string to see if it was the quit\n\ + # command. If so, intercept it, so that it doesn't\n\ + # close stdin on us!\n\ + if (temp_str.lower() == \"quit()\"):\n\ + self.loop_exit = True\n\ + in_str = \"raise SystemExit \"\n\ + return in_str\n\ +\n\ + def my_raw_input (self, prompt):\n\ + stream = sys.stdout\n\ + stream.write (prompt)\n\ + stream.flush ()\n\ + try:\n\ + line = sys.stdin.readline()\n\ + except KeyboardInterrupt:\n\ + line = \" \\n\"\n\ + except (SystemExit, EOFError):\n\ + line = \"quit()\\n\"\n\ + if not line:\n\ + raise EOFError\n\ + if line[-1] == '\\n':\n\ + line = line[:-1]\n\ + return line\n\ +\n\ + def read_py_command(self):\n\ + # Read off a complete Python command.\n\ + more = 0\n\ + while 1:\n\ + if more:\n\ + prompt = sys.ps2\n\ + else:\n\ + prompt = sys.ps1\n\ + line = self.my_raw_input(prompt)\n\ + # Can be None if sys.stdin was redefined\n\ + encoding = getattr(sys.stdin, \"encoding\", None)\n\ + if encoding and not isinstance(line, unicode):\n\ + line = line.decode(encoding)\n\ + line = self.process_input (line)\n\ + more = self.push(line)\n\ + if not more:\n\ + break\n\ +\n\ +def run_python_interpreter (dict):\n\ + # Pass in the dictionary, for continuity from one session to the next.\n\ + repl = SimpleREPL('>>> ', dict)\n\ + repl.interact()\n"; + +static int +_check_and_flush (FILE *stream) +{ + int prev_fail = ferror (stream); + return fflush (stream) || prev_fail ? EOF : 0; +} + +ScriptInterpreterPython::ScriptInterpreterPython () : + ScriptInterpreter (eScriptLanguagePython), + m_compiled_module (NULL), + m_termios_valid (false) +{ + + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + // Find the module that owns this code and use that path we get to + // set the PYTHONPATH appropriately. + + FileSpec this_module (Host::GetModuleFileSpecForHostAddress ((void *)init_lldb)); + std::string python_path; + + if (this_module.GetDirectory()) + { + // Append the directory that the module that loaded this code + // belongs to + python_path += this_module.GetDirectory().AsCString(""); + +#if defined (__APPLE__) + // If we are running on MacOSX we might be in a framework and should + // add an appropriate path so Resource can be found in a bundle + + if (::strstr(this_module.GetDirectory().AsCString(""), ".framework")) + { + python_path.append(1, ':'); + python_path.append(this_module.GetDirectory().AsCString("")); + python_path.append("/Resources/Python"); + } +#endif + // The the PYTHONPATH environment variable so that Python can find + // our lldb.py module and our _lldb.so. + ::setenv ("PYTHONPATH", python_path.c_str(), 1); + } + + Py_Initialize (); + + PyObject *compiled_module = Py_CompileString (embedded_interpreter_string, "embedded_interpreter.py", + Py_file_input); + + m_compiled_module = compiled_module; + + init_lldb (); + + // Update the path python uses to search for modules to include the current directory. + + int success = PyRun_SimpleString ("import sys"); + success = PyRun_SimpleString ("sys.path.append ('.')"); + if (success == 0) + { + // Import the Script Bridge module. + success = PyRun_SimpleString ("from lldb import *"); + } + + const char *pty_slave_name = GetScriptInterpreterPtyName (); + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + + PyObject *pmod = PyImport_ExecCodeModule((char *)"embedded_interpreter", m_compiled_module); + if (pmod != NULL) + { + PyRun_SimpleString ("ConsoleDict = locals()"); + PyRun_SimpleString ("from embedded_interpreter import run_python_interpreter"); + PyRun_SimpleString ("import sys"); + PyRun_SimpleString ("from termios import *"); + PyRun_SimpleString ("old_stdin = sys.stdin"); + + StreamString run_string; + run_string.Printf ("new_stdin = open('%s', 'r')", pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + PyRun_SimpleString ("sys.stdin = new_stdin"); + + PyRun_SimpleString ("old_stdout = sys.stdout"); + + if (out_fh != NULL) + { + PyObject *new_sysout = PyFile_FromFile (out_fh, (char *) "", (char *) "w", + _check_and_flush); + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if ((new_sysout != NULL) + && (sysmod != NULL) + && (sysdict != NULL)) + { + PyDict_SetItemString (sysdict, "stdout", new_sysout); + } + + if (PyErr_Occurred()) + PyErr_Clear(); + } + + PyRun_SimpleString ("new_mode = tcgetattr(new_stdin)"); + PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON"); + PyRun_SimpleString ("new_mode[6][VEOF] = 255"); + PyRun_SimpleString ("tcsetattr (new_stdin, TCSANOW, new_mode)"); + } + + +} + +ScriptInterpreterPython::~ScriptInterpreterPython () +{ + PyRun_SimpleString ("sys.stdin = old_stdin"); + PyRun_SimpleString ("sys.stdout = old_stdout"); + Py_Finalize (); +} + +void +ScriptInterpreterPython::ExecuteOneLine (const std::string& line, FILE *out, FILE *err) +{ + int success; + + success = PyRun_SimpleString (line.c_str()); + if (success != 0) + { + fprintf (err, "error: python failed attempting to evaluate '%s'\n", line.c_str()); + } +} + + + +size_t +ScriptInterpreterPython::InputReaderCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + if (baton == NULL) + return 0; + + ScriptInterpreterPython *interpreter = (ScriptInterpreterPython *) baton; + switch (notification) + { + case eInputReaderActivate: + { + // Save terminal settings if we can + interpreter->m_termios_valid = ::tcgetattr (::fileno (reader->GetInputFileHandle()), + &interpreter->m_termios) == 0; + struct termios tmp_termios; + if (::tcgetattr (::fileno (reader->GetInputFileHandle()), &tmp_termios) == 0) + { + tmp_termios.c_cc[VEOF] = _POSIX_VDISABLE; + ::tcsetattr (::fileno (reader->GetInputFileHandle()), TCSANOW, &tmp_termios); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len) + { + if ((int) bytes[0] == 4) + ::write (interpreter->GetMasterFileDescriptor(), "quit()", 6); + else + ::write (interpreter->GetMasterFileDescriptor(), bytes, bytes_len); + } + ::write (interpreter->GetMasterFileDescriptor(), "\n", 1); + break; + + case eInputReaderDone: + // Send a control D to the script interpreter + //::write (interpreter->GetMasterFileDescriptor(), "\nquit()\n", strlen("\nquit()\n")); + // Write a newline out to the reader output + //::fwrite ("\n", 1, 1, out_fh); + // Restore terminal settings if they were validly saved + if (interpreter->m_termios_valid) + { + ::tcsetattr (::fileno (reader->GetInputFileHandle()), + TCSANOW, + &interpreter->m_termios); + } + break; + } + + return bytes_len; +} + + +void +ScriptInterpreterPython::ExecuteInterpreterLoop (FILE *out, FILE *err) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + InputReaderSP reader_sp (new InputReader()); + 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::GetSharedInstance().PushInputReader (reader_sp); + ExecuteOneLine ("run_python_interpreter(ConsoleDict)", out, err); + Debugger::GetSharedInstance().PopInputReader (reader_sp); + } + } +} + +bool +ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ReturnType return_type, + void *ret_value) +{ + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = globals; + PyObject *py_error = NULL; + bool ret_success; + int success; + + if (in_string != 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 (py_return != NULL) + { + switch (return_type) + { + case eCharPtr: // "char *" + { + const char format[3] = "s#"; + success = PyArg_Parse (py_return, format, (char **) &ret_value); + break; + } + case eBool: + { + const char format[2] = "b"; + success = PyArg_Parse (py_return, format, (bool *) ret_value); + break; + } + case eShortInt: + { + const char format[2] = "h"; + success = PyArg_Parse (py_return, format, (short *) ret_value); + break; + } + case eShortIntUnsigned: + { + const char format[2] = "H"; + success = PyArg_Parse (py_return, format, (unsigned short *) ret_value); + break; + } + case eInt: + { + const char format[2] = "i"; + success = PyArg_Parse (py_return, format, (int *) ret_value); + break; + } + case eIntUnsigned: + { + const char format[2] = "I"; + success = PyArg_Parse (py_return, format, (unsigned int *) ret_value); + break; + } + case eLongInt: + { + const char format[2] = "l"; + success = PyArg_Parse (py_return, format, (long *) ret_value); + break; + } + case eLongIntUnsigned: + { + const char format[2] = "k"; + success = PyArg_Parse (py_return, format, (unsigned long *) ret_value); + break; + } + case eLongLong: + { + const char format[2] = "L"; + success = PyArg_Parse (py_return, format, (long long *) ret_value); + break; + } + case eLongLongUnsigned: + { + const char format[2] = "K"; + success = PyArg_Parse (py_return, format, (unsigned long long *) ret_value); + break; + } + case eFloat: + { + const char format[2] = "f"; + success = PyArg_Parse (py_return, format, (float *) ret_value); + break; + } + case eDouble: + { + const char format[2] = "d"; + success = PyArg_Parse (py_return, format, (double *) ret_value); + break; + } + case eChar: + { + const char format[2] = "c"; + success = PyArg_Parse (py_return, format, (char *) ret_value); + break; + } + default: + {} + } + Py_DECREF (py_return); + if (success) + ret_success = true; + else + ret_success = false; + } + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + ret_success = false; + } + + return ret_success; +} + +bool +ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string) +{ + bool success = false; + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = globals; + PyObject *py_error = NULL; + + 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) + { + py_return = PyEval_EvalCode (compiled_code, globals, locals); + if (py_return != NULL) + { + success = true; + Py_DECREF (py_return); + } + } + } + } + + py_error = PyErr_Occurred (); + if (py_error != NULL) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + success = false; + } + + return success; +} + +static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end."; + +size_t +ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + static StringList commands_in_progress; + + FILE *out_fh = reader->GetOutputFileHandle(); + switch (notification) + { + case eInputReaderActivate: + { + commands_in_progress.Clear(); + if (out_fh) + { + ::fprintf (out_fh, "%s\n", g_reader_instructions); + if (reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader->GetPrompt() && out_fh) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + break; + + case eInputReaderGotToken: + { + std::string temp_string (bytes, bytes_len); + commands_in_progress.AppendString (temp_string.c_str()); + if (out_fh && !reader->IsDone() && reader->GetPrompt()) + ::fprintf (out_fh, "%s", reader->GetPrompt()); + } + break; + + case eInputReaderDone: + { + BreakpointOptions *bp_options = (BreakpointOptions *)baton; + std::auto_ptr data_ap(new BreakpointOptions::CommandData()); + data_ap->user_source.AppendList (commands_in_progress); + if (data_ap.get()) + { + ScriptInterpreter *interpreter = Debugger::GetSharedInstance().GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter) + { + if (interpreter->GenerateBreakpointCommandCallbackData (data_ap->user_source, + data_ap->script_source)) + { + if (data_ap->script_source.GetSize() == 1) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + } + } + } + else + { + // FIXME: Error processing. + } + } + } + break; + + } + + return bytes_len; +} + +void +ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) +{ + InputReaderSP reader_sp (new InputReader ()); + + 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::GetSharedInstance().PushInputReader (reader_sp); + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +bool +ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def) +{ + // Convert StringList to one long, newline delimited, const char *. + std::string function_def_string; + + int num_lines = function_def.GetSize(); + + for (int i = 0; i < num_lines; ++i) + { + function_def_string.append (function_def.GetStringAtIndex(i)); + if (function_def_string.at (function_def_string.length() - 1) != '\n') + function_def_string.append ("\n"); + + } + + return ExecuteMultipleLines (function_def_string.c_str()); +} + +bool +ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user_input, StringList &callback_data) +{ + static int num_created_functions = 0; + + user_input.RemoveBlankLines (); + int num_lines = user_input.GetSize(); + std::string last_function_call; + + // Go through lines of input looking for any function definitions. For each function definition found, + // export the function definition to Python, create a potential function call for the function, and + // mark the lines of the function to be removed from the user input. + + for (int i = 0; i < num_lines; ++i) + { + int function_start = i; + std::string current_str = user_input.GetStringAtIndex (i); + const char *current_line = current_str.c_str(); + int len = 0; + if (current_line) + len = strlen (current_line); + + // Check to see if the current line is the start of a Python function definition. + if (len > 4 && strncmp (current_line, "def ", 4) == 0) + { + // We've found the first line of a function. First, get the function name. + + // Skip over the 'def '. + char *start = (char *) current_line + 4; + + // Skip over white space. + while (start[0] == ' ' || start[0] == '\t') + ++start; + + // Find the end of the function name. + char *end = start; + while (isalnum (end[0]) || end[0] == '_') + ++end; + + int name_len = end - start; + std::string func_name = current_str.substr (4, name_len); + + // Now to find the last line of the function. That will be the first line that does not begin with + // any white space (thanks to Python's indentation rules). + ++i; + bool found = false; + while (i < num_lines && !found) + { + std::string next_str = user_input.GetStringAtIndex (i); + const char *next_line = next_str.c_str(); + if (next_line[0] != ' ' && next_line[0] != '\t') + found = true; + else + ++i; + } + if (found) + --i; // Make 'i' correspond to the last line of the function. + int function_end = i; + + // Special case: All of user_input is one big function definition. + if ((function_start == 0) && (function_end == (num_lines - 1))) + { + ExportFunctionDefinitionToInterpreter (user_input); + last_function_call = func_name + " ()"; + callback_data.AppendString (last_function_call.c_str()); + return callback_data.GetSize() > 0; + } + else + { + // Make a copy of the function definition: + StringList new_function; + for (int k = function_start; k <= function_end; ++k) + { + new_function.AppendString (user_input.GetStringAtIndex (k)); + // Mark the string to be deleted from user_input. + user_input.DeleteStringAtIndex (k); + user_input.InsertStringAtIndex (k, ""); + } + ExportFunctionDefinitionToInterpreter (new_function); + last_function_call = func_name + " ()"; + } + } + } + + // Now instead of trying to really delete the marked lines from user_input, we will just copy all the + // unmarked lines into a new StringList. + + StringList new_user_input; + + for (int i = 0; i < num_lines; ++i) + { + std::string current_string = user_input.GetStringAtIndex (i); + if (current_string.compare (0, 13, "") == 0) + continue; + + new_user_input.AppendString (current_string.c_str()); + } + + num_lines = new_user_input.GetSize(); + + if (num_lines > 0) + { + if (num_lines == 1 + && strchr (new_user_input.GetStringAtIndex(0), '\n') == NULL) + { + // If there's only one line of input, and it doesn't contain any newline characters.... + callback_data.AppendString (new_user_input.GetStringAtIndex (0)); + } + else + { + // Create the new function name. + StreamString func_name; + func_name.Printf ("lldb_bp_callback_func_%d", num_created_functions); + //std::string func_name = "lldb_bp_callback_func_" + num_created_functions; + ++num_created_functions; + + // Create the function call for the new function. + last_function_call = func_name.GetString() + " ()"; + + // Create the Python function definition line (which will have to be inserted at the beginning of + // the function). + std::string def_line = "def " + func_name.GetString() + " ():"; + + + // Indent all lines an additional four spaces (as they are now being put inside a function definition). + for (int i = 0; i < num_lines; ++i) + { + const char *temp_cstring = new_user_input.GetStringAtIndex(i); + std::string temp2 = " "; + temp2.append(temp_cstring); + new_user_input.DeleteStringAtIndex (i); + new_user_input.InsertStringAtIndex (i, temp2.c_str()); + } + + // Insert the function definition line at the top of the new function. + new_user_input.InsertStringAtIndex (0, def_line.c_str()); + + ExportFunctionDefinitionToInterpreter (new_user_input); + callback_data.AppendString (last_function_call.c_str()); + } + } + else + { + if (!last_function_call.empty()) + callback_data.AppendString (last_function_call.c_str()); + } + + return callback_data.GetSize() > 0; +} + +bool +ScriptInterpreterPython::BreakpointCallbackFunction +( + void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id +) +{ + bool ret_value = true; + bool temp_bool; + + BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) baton; + + const char *python_string = bp_option_data->script_source.GetStringAtIndex(0); + + if (python_string != NULL) + { + bool success = + Debugger::GetSharedInstance().GetCommandInterpreter().GetScriptInterpreter()->ExecuteOneLineWithReturn + (python_string, + ScriptInterpreter::eBool, + (void *) &temp_bool); + if (success) + ret_value = temp_bool; + } + + return ret_value; +} diff --git a/lldb/source/Interpreter/StateVariable.cpp b/lldb/source/Interpreter/StateVariable.cpp new file mode 100644 index 000000000000..3f3c3fdb1e0e --- /dev/null +++ b/lldb/source/Interpreter/StateVariable.cpp @@ -0,0 +1,320 @@ +//===-- StateVariable.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" +#include "lldb/Interpreter/CommandInterpreter.h" + + +#include "lldb/Interpreter/StateVariable.h" + +using namespace lldb; +using namespace lldb_private; + +// Variables with integer values. + +StateVariable::StateVariable +( + const char *name, + int value, + const char *help, + Callback func_ptr +) : + m_name (name), + m_type (eTypeInteger), + m_help_text (help), + m_verification_func_ptr (func_ptr) +{ + m_int_value = value; +} + +// Variables with boolean values. + +StateVariable::StateVariable +( + const char *name, + bool value, + const char *help, + Callback func_ptr + ) : + m_name (name), + m_type (eTypeBoolean), + m_help_text (help), + m_verification_func_ptr (func_ptr) +{ + m_int_value = value; +} + +// Variables with string values. + +StateVariable::StateVariable +( + const char *name, + const char *value, + bool can_append, + const char *help, + Callback func_ptr + ) : + m_name (name), + m_type (eTypeString), + m_int_value (0), + m_string_values (), + m_help_text (help), + m_verification_func_ptr (func_ptr) +{ + m_string_values.AppendArgument(value); +} + +// Variables with array of strings values. + +StateVariable::StateVariable +( + const char *name, + const Args *args, + const char *help, + Callback func_ptr + ) : + m_name (name), + m_type (eTypeStringArray), + m_help_text (help), + m_string_values(), + m_verification_func_ptr (func_ptr) +{ + if (args) + m_string_values = *args; +} + +StateVariable::~StateVariable () +{ +} + +const char * +StateVariable::GetName () const +{ + return m_name.c_str(); +} + +StateVariable::Type +StateVariable::GetType () const +{ + return m_type; +} + +int +StateVariable::GetIntValue () const +{ + return m_int_value; +} + +bool +StateVariable::GetBoolValue () const +{ + return m_int_value; +} + +const char * +StateVariable::GetStringValue () const +{ + return m_string_values.GetArgumentAtIndex(0); +} + +const Args & +StateVariable::GetArgs () const +{ + return m_string_values; +} + +Args & +StateVariable::GetArgs () +{ + return m_string_values; +} + +const char * +StateVariable::GetHelp () const +{ + return m_help_text.c_str(); +} + +void +StateVariable::SetHelp (const char *help) +{ + m_help_text = help; +} + +void +StateVariable::AppendVariableInformation (CommandReturnObject &result) +{ + switch (m_type) + { + case eTypeBoolean: + if (m_int_value) + result.AppendMessageWithFormat (" %s (bool) = True\n", m_name.c_str()); + else + result.AppendMessageWithFormat (" %s (bool) = False\n", m_name.c_str()); + break; + + case eTypeInteger: + result.AppendMessageWithFormat (" %s (int) = %d\n", m_name.c_str(), m_int_value); + break; + + case eTypeString: + { + const char *cstr = m_string_values.GetArgumentAtIndex(0); + if (cstr && cstr[0]) + result.AppendMessageWithFormat (" %s (str) = '%s'\n", m_name.c_str(), cstr); + else + result.AppendMessageWithFormat (" %s (str) = \n", m_name.c_str()); + } + break; + + case eTypeStringArray: + { + const size_t argc = m_string_values.GetArgumentCount(); + result.AppendMessageWithFormat (" %s (string vector):\n", m_name.c_str()); + for (size_t i = 0; i < argc; ++i) + result.AppendMessageWithFormat (" [%d] %s\n", i, m_string_values.GetArgumentAtIndex(i)); + } + break; + + default: + break; + } +} + +void +StateVariable::SetStringValue (const char *new_value) +{ + if (m_string_values.GetArgumentCount() > 0) + m_string_values.ReplaceArgumentAtIndex(0, new_value); + else + m_string_values.AppendArgument(new_value); +} + +void +StateVariable::SetIntValue (int new_value) +{ + m_int_value = new_value; +} + +void +StateVariable::SetBoolValue (bool new_value) +{ + m_int_value = new_value; +} + +void +StateVariable::AppendStringValue (const char *cstr) +{ + if (cstr && cstr[0]) + { + if (m_string_values.GetArgumentCount() == 0) + { + m_string_values.AppendArgument(cstr); + } + else + { + const char *curr_arg = m_string_values.GetArgumentAtIndex(0); + if (curr_arg != NULL) + { + std::string new_arg_str(curr_arg); + new_arg_str += " "; + new_arg_str += cstr; + m_string_values.ReplaceArgumentAtIndex(0, new_arg_str.c_str()); + } + else + { + m_string_values.ReplaceArgumentAtIndex(0, cstr); + } + } + } +} + +bool +StateVariable::VerifyValue (CommandInterpreter *interpreter, void *data, CommandReturnObject &result) +{ + return (*m_verification_func_ptr) (interpreter, data, result); +} + +//void +//StateVariable::SetArrayValue (STLStringArray &new_value) +//{ +// m_string_values.AppendArgument.append(cstr); +// +// if (m_array_value != NULL) +// { +// if (m_array_value->size() > 0) +// { +// m_array_value->clear(); +// } +// } +// else +// m_array_value = new STLStringArray; +// +// for (int i = 0; i < new_value.size(); ++i) +// m_array_value->push_back (new_value[i]); +//} +// + +void +StateVariable::ArrayClearValues () +{ + m_string_values.Clear(); +} + + +void +StateVariable::ArrayAppendValue (const char *cstr) +{ + m_string_values.AppendArgument(cstr); +} + + +bool +StateVariable::HasVerifyFunction () +{ + return (m_verification_func_ptr != NULL); +} + +// Verification functions for various command interpreter variables. + +bool +StateVariable::VerifyScriptLanguage (CommandInterpreter *interpreter, void *data, CommandReturnObject &result) +{ + bool valid_lang = true; + interpreter->SetScriptLanguage (Args::StringToScriptLanguage((char *) data, eScriptLanguageDefault, &valid_lang)); + return valid_lang; +} + +bool +StateVariable::BroadcastPromptChange (CommandInterpreter *interpreter, void *data, CommandReturnObject &result) +{ + char *prompt = (char *) data; + if (prompt != NULL) + { + std::string tmp_prompt = prompt ; + int len = tmp_prompt.size(); + if (len > 1 + && (tmp_prompt[0] == '\'' || tmp_prompt[0] == '"') + && (tmp_prompt[len-1] == tmp_prompt[0])) + { + tmp_prompt = tmp_prompt.substr(1,len-2); + } + len = tmp_prompt.size(); + if (tmp_prompt[len-1] != ' ') + tmp_prompt.append(" "); + strcpy (prompt, tmp_prompt.c_str()); + data = (void *) prompt; + } + EventSP new_event_sp; + new_event_sp.reset (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (prompt))); + interpreter->BroadcastEvent (new_event_sp); + + return true; +} + diff --git a/lldb/source/Interpreter/embedded_interpreter.py b/lldb/source/Interpreter/embedded_interpreter.py new file mode 100644 index 000000000000..38b2f9ef01e6 --- /dev/null +++ b/lldb/source/Interpreter/embedded_interpreter.py @@ -0,0 +1,90 @@ +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()"): + 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 run_python_interpreter (dict): + # Pass in the dictionary, for continuity from one session to the next. + repl = SimpleREPL('>>> ', dict) + repl.interact() diff --git a/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp new file mode 100644 index 000000000000..09cc09d024ca --- /dev/null +++ b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp @@ -0,0 +1,578 @@ +//===-- 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/Scalar.h" +#include "lldb/Symbol/ClangASTContext.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; + +static const char *pluginName = "ABIMacOSX_i386"; +static const char *pluginDesc = "Mac OS X ABI for i386 targets"; +static const char *pluginShort = "abi.macosx-i386"; + +size_t +ABIMacOSX_i386::GetRedZoneSize () const +{ + return 0; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +lldb_private::ABI * +ABIMacOSX_i386::CreateInstance (const ConstString &triple) +{ + llvm::StringRef tripleStr(triple.GetCString()); + llvm::Triple llvmTriple(tripleStr); + + if (llvmTriple.getArch() != llvm::Triple::x86) + return NULL; + + return new ABIMacOSX_i386; +} + +bool +ABIMacOSX_i386::PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + + uint32_t ebpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t eipID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t espID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // Make room for the argument on the stack + + sp -= 4; + + // Align the SP + + sp &= ~(0xfull); // 16-byte alignment + + // Write the argument on the stack + + uint32_t argU32 = arg & 0xffffffffull; + Error error; + if (thread.GetProcess().WriteMemory (sp, &argU32, sizeof(argU32), error) != sizeof(argU32)) + return false; + + // The return address is pushed onto the stack. + + sp -= 4; + uint32_t returnAddressU32 = returnAddress; + if (thread.GetProcess().WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(espID, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(ebpID, 0x00000000)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(eipID, functionAddress)) + return false; + + return true; +} + +bool +ABIMacOSX_i386::PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + Error error; + uint32_t ebpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t eipID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t espID = 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: + default: + 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: + switch (val->GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *val_type = val->GetOpaqueClangQualType(); + uint32_t cstr_length; + + if (ClangASTContext::IsCStringType (val_type, 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 (thread.GetProcess().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 &= ~(0xfull); // 16-byte alignment + + // Write the arguments on the stack + + size_t numChunks = argLayout.size(); + + for (index = 0; index < numChunks; ++index) + if (thread.GetProcess().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 = returnAddress; + if (thread.GetProcess().WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(espID, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(ebpID, 0x00000000)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(eipID, functionAddress)) + return false; + + return true; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Process &process, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + uint64_t arg_contents; + uint32_t read_data; + Error error; + + if (bit_width > 32) + { + if (process.ReadMemory(current_stack_argument, &read_data, sizeof(read_data), error) != sizeof(read_data)) + return false; + + arg_contents = read_data; + + if (process.ReadMemory(current_stack_argument + 4, &read_data, sizeof(read_data), error) != sizeof(read_data)) + return false; + + arg_contents |= ((uint64_t)read_data) << 32; + + current_stack_argument += 8; + } + else { + if (process.ReadMemory(current_stack_argument, &read_data, sizeof(read_data), error) != sizeof(read_data)) + return false; + + arg_contents = read_data; + + current_stack_argument += 4; + } + + if (is_signed) + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (int8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (int16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (int32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (int64_t)arg_contents; + break; + } + } + else + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (uint8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (uint16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (uint32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (uint64_t)arg_contents; + break; + } + } + + return true; +} + +bool +ABIMacOSX_i386::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Extract the Clang AST context from the PC so that we can figure out type + // sizes + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + 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? + switch (value->GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *value_type = value->GetOpaqueClangQualType(); + bool is_signed; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + + ReadIntegerArgument(value->GetScalar(), + bit_width, + is_signed, + thread.GetProcess(), + current_stack_argument); + } + else if (ClangASTContext::IsPointerType (value_type)) + { + ReadIntegerArgument(value->GetScalar(), + 32, + false, + thread.GetProcess(), + current_stack_argument); + } + } + break; + } + } + + return true; +} + +bool +ABIMacOSX_i386::GetReturnValue (Thread &thread, + Value &value) const +{ + switch (value.GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + // Extract the Clang AST context from the PC so that we can figure out type + // sizes + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + void *value_type = value.GetOpaqueClangQualType(); + bool is_signed; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->reg; + unsigned edx_id = reg_ctx->GetRegisterInfoByName("edx", 0)->reg; + + switch (bit_width) + { + default: + case 128: + // Scalar can't hold 128-bit literals, so we don't handle this + return false; + 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 (ClangASTContext::IsPointerType (value_type)) + { + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->reg; + uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + value.GetScalar() = ptr; + } + else + { + // not handled yet + return false; + } + } + break; + } + + return true; +} + +void +ABIMacOSX_i386::Initialize() +{ + PluginManager::RegisterPlugin (pluginName, + pluginDesc, + CreateInstance); +} + +void +ABIMacOSX_i386::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ABIMacOSX_i386::GetPluginName() +{ + return pluginName; +} + +const char * +ABIMacOSX_i386::GetShortPluginName() +{ + return pluginShort; +} + +uint32_t +ABIMacOSX_i386::GetPluginVersion() +{ + return 1; +} + +void +ABIMacOSX_i386::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ABIMacOSX_i386::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ABIMacOSX_i386::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} diff --git a/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h new file mode 100644 index 000000000000..c27569b5a147 --- /dev/null +++ b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h @@ -0,0 +1,93 @@ +//===-- 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" + +namespace lldb_private { + + class ABIMacOSX_i386 : + public lldb_private::ABI + { + public: + ~ABIMacOSX_i386() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const; + + virtual bool + PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const; + + virtual bool + GetReturnValue (Thread &thread, + Value &value) const; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ABI * + CreateInstance (const ConstString &triple); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + private: + ABIMacOSX_i386() : lldb_private::ABI() { } // Call CreateInstance instead. + }; + +} // namespace lldb_private + +#endif // liblldb_ABI_h_ diff --git a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp new file mode 100644 index 000000000000..004be60c3778 --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -0,0 +1,412 @@ +//===-- 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/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/ClangASTContext.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; + +static const char *pluginName = "ABISysV_x86_64"; +static const char *pluginDesc = "System V ABI for x86_64 targets"; +static const char *pluginShort = "abi.sysv-x86_64"; + +size_t +ABISysV_x86_64::GetRedZoneSize () const +{ + return 128; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +lldb_private::ABI * +ABISysV_x86_64::CreateInstance (const ConstString &triple) +{ + llvm::StringRef tripleStr(triple.GetCString()); + llvm::Triple llvmTriple(tripleStr); + + if (llvmTriple.getArch() != llvm::Triple::x86_64) + return NULL; + + return new ABISysV_x86_64; +} + +bool +ABISysV_x86_64::PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + + uint32_t rdiID = reg_ctx->GetRegisterInfoByName("rdi", 0)->reg; + uint32_t rbpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t ripID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t rspID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // The argument is in %rdi, and not on the stack. + + if (!reg_ctx->WriteRegisterFromUnsigned(rdiID, arg)) + return false; + + // First, align the SP + + sp &= ~(0xfull); // 16-byte alignment + + // The return address is pushed onto the stack. + + sp -= 8; + uint64_t returnAddressU64 = returnAddress; + Error error; + if (thread.GetProcess().WriteMemory (sp, &returnAddressU64, sizeof(returnAddressU64), error) != sizeof(returnAddressU64)) + return false; + + // %rsp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(rspID, sp)) + return false; + + // %rbp is set to a fake value, in our case 0x0000000000000000. + + if (!reg_ctx->WriteRegisterFromUnsigned(rbpID, 0x000000000000000)) + return false; + + // %rip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(ripID, functionAddress)) + return false; + + return true; +} + +bool +ABISysV_x86_64::PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const +{ + return false; +} + +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 + + uint64_t arg_contents; + + if (current_argument_register < 6) + { + arg_contents = thread.GetRegisterContext()->ReadRegisterAsUnsigned(argument_register_ids[current_argument_register], 0); + current_argument_register++; + } + else + { + uint8_t arg_data[sizeof(arg_contents)]; + Error error; + thread.GetProcess().ReadMemory(current_stack_argument, arg_data, sizeof(arg_contents), error); + DataExtractor arg_data_extractor(arg_data, sizeof(arg_contents), thread.GetProcess().GetByteOrder(), thread.GetProcess().GetAddressByteSize()); + uint32_t offset = 0; + arg_contents = arg_data_extractor.GetMaxU64(&offset, bit_width / 8); + if (!offset) + return false; + current_stack_argument += (bit_width / 8); + } + + if (is_signed) + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (int8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (int16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (int32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (int64_t)arg_contents; + break; + } + } + else + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (uint8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (uint16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (uint32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (uint64_t)arg_contents; + break; + } + } + + return true; +} + +bool +ABISysV_x86_64::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // For now, assume that the types in the AST values come from the Target's + // scratch AST. + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + 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)->reg; + argument_register_ids[1] = reg_ctx->GetRegisterInfoByName("rsi", 0)->reg; + argument_register_ids[2] = reg_ctx->GetRegisterInfoByName("rdx", 0)->reg; + argument_register_ids[3] = reg_ctx->GetRegisterInfoByName("rcx", 0)->reg; + argument_register_ids[4] = reg_ctx->GetRegisterInfoByName("r8", 0)->reg; + argument_register_ids[5] = reg_ctx->GetRegisterInfoByName("r9", 0)->reg; + + 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? + switch (value->GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *value_type = value->GetOpaqueClangQualType(); + bool is_signed; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + + ReadIntegerArgument(value->GetScalar(), + bit_width, + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (ClangASTContext::IsPointerType (value_type)) + { + ReadIntegerArgument(value->GetScalar(), + 64, + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + break; + } + } + + return true; +} + +bool +ABISysV_x86_64::GetReturnValue (Thread &thread, + Value &value) const +{ + switch (value.GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *value_type = value.GetOpaqueClangQualType(); + bool is_signed; + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + if (!reg_ctx) + return false; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + // For now, assume that the types in the AST values come from the Target's + // scratch AST. + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Extract the register context so we can read arguments from registers + + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->reg; + + switch (bit_width) + { + default: + case 128: + // Scalar can't hold 128-bit literals, so we don't handle this + return false; + case 64: + if (is_signed) + value.GetScalar() = (int64_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0)); + else + value.GetScalar() = (uint64_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0)); + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffffffff); + else + value.GetScalar() = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffffffff); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffff); + else + value.GetScalar() = (uint16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffff); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xff); + else + value.GetScalar() = (uint8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xff); + break; + } + } + else if (ClangASTContext::IsPointerType (value_type)) + { + unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->reg; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); + } + else + { + // not handled yet + return false; + } + } + break; + } + + return true; +} + +void +ABISysV_x86_64::Initialize() +{ + PluginManager::RegisterPlugin (pluginName, + pluginDesc, + CreateInstance); +} + +void +ABISysV_x86_64::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ABISysV_x86_64::GetPluginName() +{ + return pluginName; +} + +const char * +ABISysV_x86_64::GetShortPluginName() +{ + return pluginShort; +} + +uint32_t +ABISysV_x86_64::GetPluginVersion() +{ + return 1; +} + +void +ABISysV_x86_64::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ABISysV_x86_64::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ABISysV_x86_64::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} diff --git a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h new file mode 100644 index 000000000000..b89d7c6581cb --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -0,0 +1,92 @@ +//===-- 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" + +namespace lldb_private { + +class ABISysV_x86_64 : + public lldb_private::ABI +{ +public: + ~ABISysV_x86_64() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const; + + virtual bool + PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const; + + virtual bool + GetReturnValue (Thread &thread, + Value &value) const; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ABI * + CreateInstance (const ConstString &triple); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: +private: + ABISysV_x86_64() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +} // namespace lldb_private + +#endif // liblldb_ABI_h_ diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp new file mode 100644 index 000000000000..db2d561b24ab --- /dev/null +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp @@ -0,0 +1,468 @@ +//===-- DisassemblerLLVM.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DisassemblerLLVM.h" + +#include "llvm-c/EnhancedDisassembly.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.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 +#include + +using namespace lldb; +using namespace lldb_private; + + +static +int DataExtractorByteReader(uint8_t *byte, uint64_t address, void *arg) +{ + DataExtractor &extractor = *((DataExtractor *)arg); + + if (extractor.ValidOffset(address)) + { + *byte = *(extractor.GetDataStart() + address); + return 0; + } + else + { + return -1; + } +} + +namespace { + struct RegisterReaderArg { + const lldb::addr_t instructionPointer; + const EDDisassemblerRef disassembler; + + RegisterReaderArg(lldb::addr_t ip, + EDDisassemblerRef dis) : + instructionPointer(ip), + disassembler(dis) + { + } + }; +} + +static int IPRegisterReader(uint64_t *value, unsigned regID, void* arg) +{ + uint64_t instructionPointer = ((RegisterReaderArg*)arg)->instructionPointer; + EDDisassemblerRef disassembler = ((RegisterReaderArg*)arg)->disassembler; + + if(EDRegisterIsProgramCounter(disassembler, regID)) { + *value = instructionPointer; + return 0; + } + + return -1; +} + +DisassemblerLLVM::Instruction::Instruction(EDDisassemblerRef disassembler) : + Disassembler::Instruction (), + m_disassembler (disassembler) +{ +} + +DisassemblerLLVM::Instruction::~Instruction() +{ +} + +static void +PadString(Stream *s, const std::string &str, size_t width) +{ + int diff = width - str.length(); + + if (diff > 0) + s->Printf("%s%*.*s", str.c_str(), diff, diff, ""); + else + s->Printf("%s ", str.c_str()); +} + +void +DisassemblerLLVM::Instruction::Dump +( + Stream *s, + lldb::addr_t base_address, + DataExtractor *bytes, + uint32_t bytes_offset, + const lldb_private::ExecutionContext exe_ctx, + bool raw +) +{ + const size_t opcodeColumnWidth = 7; + const size_t operandColumnWidth = 25; + + // If we have an address, print it out + if (base_address != LLDB_INVALID_ADDRESS) + s->Printf("0x%llx: ", base_address); + + // If we are supposed to show bytes, "bytes" will be non-NULL. + if (bytes) + { + uint32_t bytes_dumped = bytes->Dump(s, bytes_offset, eFormatBytes, 1, EDInstByteSize(m_inst), UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0) - bytes_offset; + // Allow for 8 bytes of opcodes normally + const uint32_t default_num_opcode_bytes = 9; + if (bytes_dumped * 3 < (default_num_opcode_bytes*3)) + { + uint32_t indent_level = (default_num_opcode_bytes*3) - (bytes_dumped * 3); + s->Printf("%*.*s", indent_level, indent_level, ""); + } + } + + int numTokens = EDNumTokens(m_inst); + + int currentOpIndex = -1; + + RegisterReaderArg rra(base_address + EDInstByteSize(m_inst), m_disassembler); + + lldb_private::Process *process = exe_ctx.process; + + bool printTokenized = false; + + if (numTokens != -1) + { + printTokenized = true; + + // Handle the opcode column. + + StreamString opcode; + + int tokenIndex = 0; + + EDTokenRef token; + const char *tokenStr; + + if (EDGetToken(&token, m_inst, tokenIndex)) + printTokenized = false; + + if (!printTokenized || !EDTokenIsOpcode(token)) + printTokenized = false; + + if (!printTokenized || EDGetTokenString(&tokenStr, token)) + printTokenized = false; + + // Put the token string into our opcode string + opcode.PutCString(tokenStr); + + // If anything follows, it probably starts with some whitespace. Skip it. + + tokenIndex++; + + if (printTokenized && tokenIndex < numTokens) + { + if(!printTokenized || EDGetToken(&token, m_inst, tokenIndex)) + printTokenized = false; + + if(!printTokenized || !EDTokenIsWhitespace(token)) + printTokenized = false; + } + + tokenIndex++; + + // Handle the operands and the comment. + + StreamString operands; + StreamString comment; + + if (printTokenized) + { + bool show_token; + + for (; tokenIndex < numTokens; ++tokenIndex) + { + if (EDGetToken(&token, m_inst, tokenIndex)) + return; + + if (raw) + { + show_token = true; + } + else + { + int operandIndex = EDOperandIndexForToken(token); + + if (operandIndex >= 0) + { + if (operandIndex != currentOpIndex) + { + show_token = true; + + currentOpIndex = operandIndex; + EDOperandRef operand; + + if (!EDGetOperand(&operand, m_inst, currentOpIndex)) + { + if (EDOperandIsMemory(operand)) + { + uint64_t operand_value; + + if (!EDEvaluateOperand(&operand_value, operand, IPRegisterReader, &rra)) + { + if (EDInstIsBranch(m_inst)) + { + operands.Printf("0x%llx ", operand_value); + show_token = false; + } + else + { + // Put the address value into the comment + comment.Printf("0x%llx ", operand_value); + } + + lldb_private::Address so_addr; + if (process) + { + if (process->ResolveLoadAddress(operand_value, so_addr)) + { + so_addr.Dump(&comment, process, Address::DumpStyleResolvedDescription, Address::DumpStyleSectionNameOffset); + } + } + } // EDEvaluateOperand + } // EDOperandIsMemory + } // EDGetOperand + } // operandIndex != currentOpIndex + } // operandIndex >= 0 + } // else(raw) + + if (show_token) + { + if(EDGetTokenString(&tokenStr, token)) + { + printTokenized = false; + break; + } + + operands.PutCString(tokenStr); + } + } // for (tokenIndex) + + if (printTokenized) + { + if (operands.GetString().empty()) + { + s->PutCString(opcode.GetString().c_str()); + } + else + { + PadString(s, opcode.GetString(), opcodeColumnWidth); + + if (comment.GetString().empty()) + { + s->PutCString(operands.GetString().c_str()); + } + else + { + PadString(s, operands.GetString(), operandColumnWidth); + + s->PutCString("; "); + s->PutCString(comment.GetString().c_str()); + } // else (comment.GetString().empty()) + } // else (operands.GetString().empty()) + } // printTokenized + } // for (tokenIndex) + } // numTokens != -1 + + if (!printTokenized) + { + const char *str; + + if (EDGetInstString(&str, m_inst)) + return; + else + s->PutCString(str); + } +} + +bool +DisassemblerLLVM::Instruction::DoesBranch() const +{ + return EDInstIsBranch(m_inst); +} + +size_t +DisassemblerLLVM::Instruction::GetByteSize() const +{ + return EDInstByteSize(m_inst); +} + +size_t +DisassemblerLLVM::Instruction::Extract(const DataExtractor &data, uint32_t data_offset) +{ + if (EDCreateInsts(&m_inst, 1, m_disassembler, DataExtractorByteReader, data_offset, (void*)(&data))) + return EDInstByteSize(m_inst); + else + return 0; +} + +static inline const char * +TripleForCPU(cpu_type_t cpuType) +{ + switch (cpuType) + { + default: + return NULL; + case CPU_TYPE_X86: + return "i386-unknown-unknown"; + case CPU_TYPE_X86_64: + return "x86_64-unknown-unknown"; + } +} + +static inline EDAssemblySyntax_t +SyntaxForCPU(cpu_type_t cpuType) +{ + switch (cpuType) + { + default: + return (EDAssemblySyntax_t)0; // default + case CPU_TYPE_X86: + case CPU_TYPE_X86_64: + return kEDAssemblySyntaxX86ATT; + } +} + +Disassembler * +DisassemblerLLVM::CreateInstance(const ArchSpec &arch) +{ + cpu_type_t cpuType = arch.GetCPUType(); + + if (TripleForCPU(cpuType)) + return new DisassemblerLLVM(arch); + else + return NULL; +} + +DisassemblerLLVM::DisassemblerLLVM(const ArchSpec &arch) : + Disassembler(arch) +{ + cpu_type_t cpuType = arch.GetCPUType(); + + const char *triple = TripleForCPU(cpuType); + assert(triple && "Unhandled CPU type!"); + + EDAssemblySyntax_t syntax = SyntaxForCPU(cpuType); + + assert(!EDGetDisassembler(&m_disassembler, triple, syntax) && "No disassembler created!"); +} + +DisassemblerLLVM::~DisassemblerLLVM() +{ +} + +size_t +DisassemblerLLVM::ParseInstructions +( + const DataExtractor& data, + uint32_t data_offset, + uint32_t num_instructions, + lldb::addr_t base_addr +) +{ + size_t total_inst_byte_size = 0; + + m_instruction_list.Clear(); + + while (data.ValidOffset(data_offset) && num_instructions) + { + Instruction::shared_ptr inst_sp (new Instruction(m_disassembler)); + + size_t inst_byte_size = inst_sp->Extract(data, data_offset); + + if (inst_byte_size == 0) + break; + + m_instruction_list.AppendInstruction(inst_sp); + + total_inst_byte_size += inst_byte_size; + data_offset += inst_byte_size; + num_instructions--; + } + + return total_inst_byte_size; +} + +void +DisassemblerLLVM::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DisassemblerLLVM::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +DisassemblerLLVM::GetPluginNameStatic() +{ + return "disassembler.llvm"; +} + +const char * +DisassemblerLLVM::GetPluginDescriptionStatic() +{ + return "Disassembler that uses LLVM opcode tables to disassemble i386 and x86_64."; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +DisassemblerLLVM::GetPluginName() +{ + return "DisassemblerLLVM"; +} + +const char * +DisassemblerLLVM::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DisassemblerLLVM::GetPluginVersion() +{ + return 1; +} + +void +DisassemblerLLVM::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +DisassemblerLLVM::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +DisassemblerLLVM::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h new file mode 100644 index 000000000000..98166cb558c2 --- /dev/null +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h @@ -0,0 +1,111 @@ +//===-- DisassemblerLLVM.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DisassemblerLLVM_h_ +#define liblldb_DisassemblerLLVM_h_ + +#include "lldb/Core/Disassembler.h" +#include "lldb/Host/Mutex.h" + +struct EDDisassembler; +typedef EDDisassembler *EDDisassemblerRef; + +struct EDInst; +typedef EDInst *EDInstRef; + +class DisassemblerLLVM : public lldb_private::Disassembler +{ +public: + class Instruction : public lldb_private::Disassembler::Instruction + { + public: + Instruction(EDDisassemblerRef disassembler); + + virtual + ~Instruction(); + + void + Dump (lldb_private::Stream *s, + lldb::addr_t base_address, + lldb_private::DataExtractor *bytes, + uint32_t bytes_offset, + const lldb_private::ExecutionContext exe_ctx, + bool raw); + + bool + DoesBranch () const; + + size_t + GetByteSize() const; + + size_t + Extract (const lldb_private::DataExtractor &data, + uint32_t data_offset); + + protected: + EDDisassemblerRef m_disassembler; + EDInstRef m_inst; + }; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::Disassembler * + CreateInstance(const lldb_private::ArchSpec &arch); + + + DisassemblerLLVM(const lldb_private::ArchSpec &arch); + + virtual + ~DisassemblerLLVM(); + + size_t + ParseInstructions (const lldb_private::DataExtractor& data, + uint32_t data_offset, + uint32_t num_instructions, + lldb::addr_t base_addr); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + EDDisassemblerRef m_disassembler; +}; + +#endif // liblldb_DisassemblerLLVM_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp new file mode 100644 index 000000000000..a8dbf053e3d9 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -0,0 +1,1129 @@ +//===-- DynamicLoaderMacOSXDYLD.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" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/StackFrame.h" + +#include "DynamicLoaderMacOSXDYLD.h" +#include "DynamicLoaderMacOSXDYLDLog.h" + +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#include +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; + + +/// FIXME - The ObjC Runtime trampoline handler doesn't really belong here. +/// I am putting it here so I can invoke it in the Trampoline code here, but +/// it should be moved to the ObjC Runtime support when it is set up. + +//---------------------------------------------------------------------- +// 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 * +DynamicLoaderMacOSXDYLD::CreateInstance (Process* process) +{ + return new DynamicLoaderMacOSXDYLD (process); +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) : + DynamicLoader(process), + m_dyld(), + m_dyld_all_image_infos_addr(LLDB_INVALID_ADDRESS), + m_dyld_all_image_infos(), + m_break_id(LLDB_INVALID_BREAK_ID), + m_dyld_image_infos(), + m_mutex(Mutex::eMutexTypeRecursive), + m_objc_trampoline_handler_ap(NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::~DynamicLoaderMacOSXDYLD() +{ + Clear(true); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidAttach () +{ + PrivateInitialize(m_process); + if (NeedToLocateDYLD ()) + LocateDYLD (); + SetNotificationBreakpoint (); + UpdateAllImageInfos(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidLaunch () +{ + PrivateInitialize(m_process); + if (NeedToLocateDYLD ()) + LocateDYLD (); + SetNotificationBreakpoint (); + UpdateAllImageInfos(); +} + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->ClearBreakpointSiteByID(m_break_id); + + if (clear_process) + m_process = NULL; + m_dyld.Clear(false); + m_dyld_all_image_infos_addr = LLDB_INVALID_ADDRESS; + m_dyld_all_image_infos.Clear(); + m_break_id = LLDB_INVALID_BREAK_ID; + m_dyld_image_infos.clear(); +} + +//---------------------------------------------------------------------- +// Check if we have found DYLD yet +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::DidSetNotificationBreakpoint() const +{ + return LLDB_BREAK_ID_IS_VALID (m_break_id); +} + +//---------------------------------------------------------------------- +// Try and figure out where dyld is by first asking the Process +// if it knows (which currently calls down in the the lldb::Process +// to get the DYLD info (available on SnowLeopard only). If that fails, +// then check in the default addresses. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::LocateDYLD() +{ + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS) + m_dyld_all_image_infos_addr = m_process->GetImageInfoAddress (); + + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + if (ReadAllImageInfosStructure ()) + { + if (m_dyld_all_image_infos.dyldImageLoadAddress != LLDB_INVALID_ADDRESS) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos.dyldImageLoadAddress); + else + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos_addr & 0xfffffffffff00000ull); + } + } + + // Check some default values + Module *executable = m_process->GetTarget().GetExecutableModule().get(); + + if (executable) + { + if (executable->GetArchitecture().GetAddressByteSize() == 8) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x7fff5fc00000ull); + } +#if defined (__arm__) + else + { + ArchSpec arm_arch("arm"); + if (arm_arch == executable->Arch()) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x2fe00000); + } +#endif + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x8fe00000); + } + return false; +} + +//---------------------------------------------------------------------- +// Assume that dyld is in memory at ADDR and try to parse it's load +// commands +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadDYLDInfoFromMemoryAndSetNotificationCallback(lldb::addr_t addr) +{ + DataExtractor data; // Load command data + if (ReadMachHeader (addr, &m_dyld.header, &data)) + { + if (m_dyld.header.filetype == MH_DYLINKER) + { + m_dyld.address = addr; + ModuleSP dyld_module_sp; + if (ParseLoadCommands (data, m_dyld, &m_dyld.file_spec)) + { + if (m_dyld.file_spec) + { + ArchSpec dyld_arch(m_dyld.header.cputype, m_dyld.header.cpusubtype); + dyld_module_sp = m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (m_dyld.file_spec); + + if (dyld_module_sp.get() == NULL || dyld_module_sp->GetArchitecture() != dyld_arch) + { + dyld_module_sp = m_process->GetTarget().GetSharedModule (m_dyld.file_spec, + dyld_arch, + &m_dyld.uuid); + } + + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS && dyld_module_sp.get()) + { + static ConstString g_dyld_all_image_infos ("dyld_all_image_infos"); + const Symbol *symbol = dyld_module_sp->FindFirstSymbolWithNameAndType (g_dyld_all_image_infos, eSymbolTypeData); + if (symbol) + m_dyld_all_image_infos_addr = symbol->GetValue().GetLoadAddress(m_process); + } + + // Update all image infos + UpdateAllImageInfos(); + + // If we didn't have an executable before, but now we do, then the + // dyld module shared pointer might be unique and we may need to add + // it again (since Target::SetExecutableModule() will clear the + // images). So append the dyld module back to the list if it is + /// unique! + if (m_process->GetTarget().GetImages().AppendInNeeded (dyld_module_sp)) + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + + return true; + } + } + return false; +} + +bool +DynamicLoaderMacOSXDYLD::NeedToLocateDYLD () const +{ + return m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS; +} + +bool +DynamicLoaderMacOSXDYLD::UpdateCommPageLoadAddress(Module *module) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + uint32_t num_sections = section_list->GetSize(); + for (uint32_t i=0; iGetSectionAtIndex (i).get(); + if (section) + { + const addr_t new_section_load_addr = section->GetFileAddress (); + const addr_t old_section_load_addr = m_process->GetSectionLoadAddress (section); + if (old_section_load_addr == LLDB_INVALID_ADDRESS || + old_section_load_addr != new_section_load_addr) + { + if (m_process->SectionLoaded (section, section->GetFileAddress ())) + changed = true; + } + } + } + } + } + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UpdateImageLoadAddress (Module *module, struct DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->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) + info.slide = 0; + 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... + Section *section = section_list->GetSectionAtIndex (sect_idx).get(); + if (section) + { + // Find the first section that begins at file offset zero + // a file size (skip page zero). + if (section->GetFileOffset() == 0 && section->GetFileSize() > 0) + { + // We have now found the section, lets match it up + // with the section in the dyld image info structure. + const Segment *dyld_segment = info.FindSegment (section->GetName()); + if (dyld_segment) + info.slide = info.address - dyld_segment->addr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + } + + // We now know the slide amount, so go through all sections + // and update the load addresses with the correct values. + uint32_t num_segments = info.segments.size(); + for (uint32_t i=0; iFindSectionByName(info.segments[i].name)); + assert (section_sp.get() != NULL); + const addr_t new_section_load_addr = info.segments[i].addr + info.slide; + const addr_t old_section_load_addr = m_process->GetSectionLoadAddress (section_sp.get()); + if (old_section_load_addr == LLDB_INVALID_ADDRESS || + old_section_load_addr != new_section_load_addr) + { + if (m_process->SectionLoaded (section_sp.get(), new_section_load_addr)) + changed = true; + } + } + } + } + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UnloadImageLoadAddress (Module *module, struct DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + uint32_t num_segments = info.segments.size(); + for (uint32_t i=0; iFindSectionByName(info.segments[i].name)); + assert (section_sp.get() != NULL); + const addr_t old_section_load_addr = info.segments[i].addr + info.slide; + if (m_process->SectionUnloaded (section_sp.get(), old_section_load_addr)) + changed = true; + } + } + } + } + return changed; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when our DYLD notification +// breakpoint gets hit. We update all of our image infos and then +// let our super class DynamicLoader class decide if we should stop +// or not (based on global preference). +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::NotifyBreakpointHit (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) +{ + // Let the event know that the images have changed + DynamicLoaderMacOSXDYLD* dyld_instance = (DynamicLoaderMacOSXDYLD*) baton; + dyld_instance->UpdateAllImageInfos(); + // Return true to stop the target, false to just let the target run + return dyld_instance->GetStopWhenImagesChange(); +} + +bool +DynamicLoaderMacOSXDYLD::ReadAllImageInfosStructure () +{ + Mutex::Locker locker(m_mutex); + m_dyld_all_image_infos.Clear(); + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + const ByteOrder endian = m_process->GetByteOrder(); + const uint32_t addr_size = m_process->GetAddressByteSize(); + uint8_t buf[256]; + const size_t count = 2 * sizeof(uint32_t) + // version + dylib_info_count + addr_size * 2 + // dylib_info_addr + notification + 2 + addr_size - 2 + // processDetachedFromSharedRegion + libSystemInitialized + pad + addr_size; // dyldImageLoadAddress + Error error; + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, count, error); + if (bytes_read == count) + { + DataExtractor data(buf, count, endian, addr_size); + uint32_t offset = 0; + m_dyld_all_image_infos.version = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_count = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_addr = data.GetPointer(&offset); + m_dyld_all_image_infos.notification = data.GetPointer(&offset); + m_dyld_all_image_infos.processDetachedFromSharedRegion = data.GetU8(&offset); + if (m_dyld_all_image_infos.version >= 2) + { + m_dyld_all_image_infos.libSystemInitialized = data.GetU8(&offset); + // Adjust for padding. + offset += addr_size - 2; + m_dyld_all_image_infos.dyldImageLoadAddress = data.GetPointer(&offset); + } + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// If we have found where the "_dyld_all_image_infos" lives in memory, +// read the current info from it, and then update all image load +// addresses (or lack thereof). +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::UpdateAllImageInfos() +{ + if (ReadAllImageInfosStructure ()) + { + Mutex::Locker locker(m_mutex); + uint32_t idx; + Error error; + uint32_t i = 0; + DYLDImageInfo::collection old_dyld_all_image_infos; + old_dyld_all_image_infos.swap(m_dyld_image_infos); + + // If we made it here, we are assuming that the all dylib info data should + // be valid, lets read the info array. + const ByteOrder endian = m_process->GetByteOrder(); + const uint32_t addr_size = m_process->GetAddressByteSize(); + + if (m_dyld_all_image_infos.dylib_info_count > 0) + { + if (m_dyld_all_image_infos.dylib_info_addr == 0) + { + // DYLD is updating the images right now... + } + else + { + m_dyld_image_infos.resize(m_dyld_all_image_infos.dylib_info_count); + const size_t count = m_dyld_image_infos.size() * 3 * addr_size; + DataBufferHeap info_data(count, 0); + Error error; + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos.dylib_info_addr, + info_data.GetBytes(), + info_data.GetByteSize(), + error); + if (bytes_read == count) + { + uint32_t info_data_offset = 0; + DataExtractor info_data_ref(info_data.GetBytes(), info_data.GetByteSize(), endian, addr_size); + for (i = 0; info_data_ref.ValidOffset(info_data_offset); i++) + { + assert (i < m_dyld_image_infos.size()); + m_dyld_image_infos[i].address = info_data_ref.GetPointer(&info_data_offset); + lldb::addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + m_dyld_image_infos[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + m_process->ReadMemory (path_addr, raw_path, sizeof(raw_path), error); + m_dyld_image_infos[i].file_spec.SetFile(raw_path); + } + assert(i == m_dyld_all_image_infos.dylib_info_count); + + UpdateAllImageInfosHeaderAndLoadCommands(); + } + else + { + DEBUG_PRINTF( "unable to read all data for all_dylib_infos."); + m_dyld_image_infos.clear(); + } + } + } + else + { + m_dyld_image_infos.clear(); + } + + // If our new list is smaller than our old list, we have unloaded + // some shared libraries + if (m_dyld_image_infos.size() < old_dyld_all_image_infos.size()) + { + ModuleList unloaded_module_list; + for (idx = m_dyld_image_infos.size(); idx < old_dyld_all_image_infos.size(); ++idx) + { + ModuleSP unload_image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (old_dyld_all_image_infos[idx].file_spec)); + if (unload_image_module_sp.get()) + { + if (UnloadImageLoadAddress (unload_image_module_sp.get(), old_dyld_all_image_infos[idx])) + unloaded_module_list.AppendInNeeded (unload_image_module_sp); + } + } + if (unloaded_module_list.GetSize() > 0) + m_process->GetTarget().ModulesDidUnload (unloaded_module_list); + } + } + else + { + m_dyld_image_infos.clear(); + } + + const uint32_t num_dylibs = m_dyld_image_infos.size(); + if (num_dylibs > 0) + { + ModuleList loaded_module_list; + for (uint32_t idx = 0; idxGetTarget().GetImages().FindFirstModuleForFileSpec (m_dyld_image_infos[idx].file_spec)); + if (image_module_sp.get() == NULL || image_module_sp->GetArchitecture() != arch_spec) + { + image_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[idx].file_spec, + arch_spec, + &m_dyld_image_infos[idx].uuid); + } + + if (image_module_sp) + { + ObjectFile *objfile = image_module_sp->GetObjectFile (); + if (objfile) + { + SectionList *sections = objfile->GetSectionList(); + if (sections) + { + ConstString commpage_dbstr("__commpage"); + Section *commpage_section = sections->FindSectionByName(commpage_dbstr).get(); + if (commpage_section) + { + FileSpec objfile_file_spec(objfile->GetFileSpec()); + ModuleSP commpage_image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (objfile_file_spec, &commpage_dbstr)); + if (commpage_image_module_sp.get() == NULL) + { + commpage_image_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[idx].file_spec, + arch_spec, + &m_dyld_image_infos[idx].uuid, + &commpage_dbstr, + objfile->GetOffset() + commpage_section->GetOffset()); + UpdateCommPageLoadAddress(commpage_image_module_sp.get()); + } + } + } + } + + // UpdateImageLoadAddress will return true if any segments + // change load address. We need to check this so we don't + // mention that all loaded shared libraries are newly loaded + // each time we hit out dyld breakpoint since dyld will list all + // shared libraries each time. + if (UpdateImageLoadAddress (image_module_sp.get(), m_dyld_image_infos[idx])) + { + loaded_module_list.AppendInNeeded (image_module_sp); + } + } + } + PutToLog(DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1)); + if (loaded_module_list.GetSize() > 0) + { + // FIXME: This should really be in the Runtime handlers class, which should get + // called by the target's ModulesDidLoad, but we're doing it all locally for now + // to save time. + // Also, I'm assuming there can be only one libobjc dylib loaded... + + if (m_objc_trampoline_handler_ap.get() == NULL) + { + size_t num_modules = loaded_module_list.GetSize(); + for (int i = 0; i < num_modules; i++) + { + if (ObjCTrampolineHandler::ModuleIsObjCLibrary (loaded_module_list.GetModuleAtIndex (i))) + { + m_objc_trampoline_handler_ap.reset (new ObjCTrampolineHandler(m_process->GetSP(), loaded_module_list.GetModuleAtIndex (i))); + break; + } + } + } + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + } + return m_dyld_image_infos.size(); +} + +//---------------------------------------------------------------------- +// Read a mach_header at ADDR into HEADER, and also fill in the load +// command data into LOAD_COMMAND_DATA if it is non-NULL. +// +// Returns true if we succeed, false if we fail for any reason. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadMachHeader (lldb::addr_t addr, struct mach_header *header, DataExtractor *load_command_data) +{ + DataBufferHeap header_bytes(sizeof(struct mach_header), 0); + Error error; + size_t bytes_read = m_process->ReadMemory (addr, + header_bytes.GetBytes(), + header_bytes.GetByteSize(), + error); + if (bytes_read == sizeof(struct mach_header)) + { + uint32_t offset = 0; + ::memset (header, 0, sizeof(header)); + + // Get the magic byte unswapped so we can figure out what we are dealing with + DataExtractor data(header_bytes.GetBytes(), header_bytes.GetByteSize(), eByteOrderHost, 4); + header->magic = data.GetU32(&offset); + lldb::addr_t load_cmd_addr = addr; + data.SetByteOrder(DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header->magic)); + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetAddressByteSize(4); + load_cmd_addr += sizeof(struct mach_header); + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetAddressByteSize(8); + load_cmd_addr += sizeof(struct mach_header_64); + break; + + default: + return false; + } + + // Read the rest of dyld's mach header + if (data.GetU32(&offset, &header->cputype, (sizeof(struct mach_header)/sizeof(uint32_t)) - 1)) + { + if (load_command_data == NULL) + return true; // We were able to read the mach_header and weren't asked to read the load command bytes + + DataBufferSP load_cmd_data_sp(new DataBufferHeap(header->sizeofcmds, 0)); + + size_t load_cmd_bytes_read = m_process->ReadMemory (load_cmd_addr, + load_cmd_data_sp->GetBytes(), + load_cmd_data_sp->GetByteSize(), + error); + + if (load_cmd_bytes_read == header->sizeofcmds) + { + // Set the load command data and also set the correct endian + // swap settings and the correct address size + load_command_data->SetData(load_cmd_data_sp, 0, header->sizeofcmds); + load_command_data->SetByteOrder(data.GetByteOrder()); + load_command_data->SetAddressByteSize(data.GetAddressByteSize()); + return true; // We successfully read the mach_header and the load command data + } + + return false; // We weren't able to read the load command data + } + } + return false; // We failed the read the mach_header +} + + +//---------------------------------------------------------------------- +// Parse the load commands for an image +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::ParseLoadCommands (const DataExtractor& data, struct DYLDImageInfo& dylib_info, FileSpec *lc_id_dylinker) +{ + uint32_t offset = 0; + uint32_t cmd_idx; + Segment segment; + dylib_info.Clear (true); + + for (cmd_idx = 0; cmd_idx < dylib_info.header.ncmds; cmd_idx++) + { + // Clear out any load command specific data from DYLIB_INFO since + // we are about to read it. + + if (data.ValidOffsetForDataOfSize (offset, sizeof(struct load_command))) + { + struct load_command load_cmd; + uint32_t load_cmd_offset = offset; + load_cmd.cmd = data.GetU32 (&offset); + load_cmd.cmdsize = data.GetU32 (&offset); + switch (load_cmd.cmd) + { + case LC_SEGMENT: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + segment.addr = data.GetU32 (&offset); + segment.size = data.GetU32 (&offset); + dylib_info.segments.push_back (segment); + } + break; + + case LC_SEGMENT_64: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + segment.addr = data.GetU64 (&offset); + segment.size = data.GetU64 (&offset); + dylib_info.segments.push_back (segment); + } + break; + + case LC_ID_DYLINKER: + if (lc_id_dylinker) + { + uint32_t name_offset = load_cmd_offset + data.GetU32 (&offset); + const char *path = data.PeekCStr (name_offset); + lc_id_dylinker->SetFile (path); + } + break; + + case LC_UUID: + dylib_info.uuid.SetBytes(data.GetData (&offset, 16)); + break; + + default: + break; + } + // Set offset to be the beginning of the next load command. + offset = load_cmd_offset + load_cmd.cmdsize; + } + } + return cmd_idx; +} + +//---------------------------------------------------------------------- +// Read the mach_header and load commands for each image that the +// _dyld_all_image_infos structure points to and cache the results. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::UpdateAllImageInfosHeaderAndLoadCommands() +{ + uint32_t exe_idx = UINT32_MAX; + // Read any UUID values that we can get + for (uint32_t i = 0; i < m_dyld_all_image_infos.dylib_info_count; i++) + { + if (!m_dyld_image_infos[i].UUIDValid()) + { + DataExtractor data; // Load command data + if (!ReadMachHeader (m_dyld_image_infos[i].address, &m_dyld_image_infos[i].header, &data)) + continue; + + ParseLoadCommands (data, m_dyld_image_infos[i], NULL); + + if (m_dyld_image_infos[i].header.filetype == MH_EXECUTE) + exe_idx = i; + } + } + + if (exe_idx < m_dyld_image_infos.size()) + { + bool set_executable = false; + ArchSpec dyld_exe_arch_spec(m_dyld_image_infos[exe_idx].header.cputype, m_dyld_image_infos[exe_idx].header.cpusubtype); + ModuleSP exe_module_sp(m_process->GetTarget().GetExecutableModule()); + if (exe_module_sp.get()) + { + if (exe_module_sp->GetFileSpec() != m_dyld_image_infos[exe_idx].file_spec || + exe_module_sp->GetArchitecture() != dyld_exe_arch_spec) + set_executable = true; + } + else + set_executable = true; + + if (set_executable) + { + exe_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[exe_idx].file_spec, + dyld_exe_arch_spec, + &m_dyld_image_infos[exe_idx].uuid); + if (exe_module_sp.get()) + { + // If we found the file where it purported to be, then it should + // be safe to load dependent images. + bool get_dependent_images = exe_module_sp->GetFileSpec() == m_dyld_image_infos[exe_idx].file_spec; + + m_process->GetTarget().SetExecutableModule (exe_module_sp, get_dependent_images); + } + } + } +} + +//---------------------------------------------------------------------- +// Dump a Segment to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Segment::PutToLog (Log *log, lldb::addr_t slide) const +{ + if (log) + log->Printf("\t\t%16s [0x%16.16llx - 0x%16.16llx)", name.AsCString(""), addr + slide, addr + slide + size); +} + +const DynamicLoaderMacOSXDYLD::Segment * +DynamicLoaderMacOSXDYLD::DYLDImageInfo::FindSegment (const ConstString &name) const +{ + const size_t num_segments = segments.size(); + for (size_t i=0; iPrintf("\t modtime=0x%8.8llx uuid=%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 path='%s/%s' (UNLOADED)", + mod_date, + 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], + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + log->Printf("\t modtime=0x%8.8llx path='%s/%s' (UNLOADED)", + mod_date, + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + { + if (u) + { + log->Printf("\taddress=0x%16.16llx modtime=0x%8.8llx uuid=%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 path='%s/%s'", + address, + mod_date, + 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], + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + { + log->Printf("\taddress=0x%16.16llx modtime=0x%8.8llx path='%s/%s'", + address, + mod_date, + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + + } + for (uint32_t i=0; iPrintf("dyld_all_image_infos = { version=%d, count=%d, addr=0x%8.8llx, notify=0x%8.8llx }", + m_dyld_all_image_infos.version, + m_dyld_all_image_infos.dylib_info_count, + (uint64_t)m_dyld_all_image_infos.dylib_info_addr, + (uint64_t)m_dyld_all_image_infos.notification); + size_t i; + const size_t count = m_dyld_image_infos.size(); + if (count > 0) + { + log->Printf("\tdyld_image_infos"); + for (i = 0; iPrivateInitialize(process); +} + +void +DynamicLoaderMacOSXDYLD::PrivateInitialize(Process *process) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + Clear(true); + m_process = process; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when the process state +// changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::ProcessStateChanged(void *baton, Process *process, StateType state) +{ + ((DynamicLoaderMacOSXDYLD*)baton)->PrivateProcessStateChanged(process, state); +} + +bool +DynamicLoaderMacOSXDYLD::SetNotificationBreakpoint () +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + if (m_break_id == LLDB_INVALID_BREAK_ID) + { + if (m_dyld_all_image_infos.notification != LLDB_INVALID_ADDRESS) + { + Address so_addr; + // Set the notification breakpoint and install a breakpoint + // callback function that will get called each time the + // breakpoint gets hit. We will use this to track when shared + // libraries get loaded/unloaded. + + if (m_process->ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr)) + { + Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint (so_addr, true).get(); + dyld_break->SetCallback (DynamicLoaderMacOSXDYLD::NotifyBreakpointHit, this, true); + m_break_id = dyld_break->GetID(); + } + } + } + return m_break_id != LLDB_INVALID_BREAK_ID; +} + +//----------------------------------------------------------------------Target.h + +// Member function that gets called when the process state changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PrivateProcessStateChanged (Process *process, StateType state) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s(%s)\n", __FUNCTION__, StateAsCString(state)); + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(false); + break; + + case eStateStopped: + // Keep trying find dyld and set our notification breakpoint each time + // we stop until we succeed + if (!DidSetNotificationBreakpoint () && m_process->IsAlive()) + { + if (NeedToLocateDYLD ()) + LocateDYLD (); + + SetNotificationBreakpoint (); + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} + +ThreadPlanSP +DynamicLoaderMacOSXDYLD::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP thread_plan_sp; + StackFrame *current_frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext ¤t_context = current_frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *current_symbol = current_context.symbol; + + if (current_symbol != NULL) + { + if (current_symbol->IsTrampoline()) + { + const ConstString &trampoline_name = current_symbol->GetMangled().GetName(); + if (trampoline_name) + { + SymbolContextList target_symbols; + ModuleList &images = thread.GetProcess().GetTarget().GetImages(); + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, target_symbols); + // FIXME - Make the Run to Address take multiple addresses, and + // run to any of them. + if (target_symbols.GetSize() == 1) + { + SymbolContext context; + AddressRange addr_range; + if (target_symbols.GetContextAtIndex(0, context)) + { + context.GetAddressRange (eSymbolContextEverything, addr_range); + thread_plan_sp.reset (new ThreadPlanRunToAddress (thread, addr_range.GetBaseAddress(), stop_others)); + } + } + else if (target_symbols.GetSize() > 1) + { + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1); + if (log) + { + log->Printf ("Found more than one symbol for trampoline target: \"%s\"", trampoline_name.AsCString()); + } + } + else + { + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1); + if (log) + { + log->Printf ("Could not find symbol for trampoline target: \"%s\"", trampoline_name.AsCString()); + } + } + } + } + } + + if (thread_plan_sp == NULL && m_objc_trampoline_handler_ap.get()) + thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan (thread, stop_others); + + return thread_plan_sp; +} + +void +DynamicLoaderMacOSXDYLD::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderMacOSXDYLD::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +DynamicLoaderMacOSXDYLD::GetPluginNameStatic() +{ + return "dynamic-loader.macosx-dyld"; +} + +const char * +DynamicLoaderMacOSXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in MacOSX user processes."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +DynamicLoaderMacOSXDYLD::GetPluginName() +{ + return "DynamicLoaderMacOSXDYLD"; +} + +const char * +DynamicLoaderMacOSXDYLD::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderMacOSXDYLD::GetPluginVersion() +{ + return 1; +} + +void +DynamicLoaderMacOSXDYLD::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +DynamicLoaderMacOSXDYLD::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +DynamicLoaderMacOSXDYLD::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h new file mode 100644 index 000000000000..724a8b6bd6df --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -0,0 +1,360 @@ +//===-- DynamicLoaderMacOSXDYLD.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderMacOSXDYLD_h_ +#define liblldb_DynamicLoaderMacOSXDYLD_h_ + +// C Includes +#include + +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "ObjCTrampolineHandler.h" + +class DynamicLoaderMacOSXDYLD : public lldb_private::DynamicLoader +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process); + + DynamicLoaderMacOSXDYLD (lldb_private::Process *process); + + virtual + ~DynamicLoaderMacOSXDYLD (); + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach (); + + virtual void + DidLaunch (); + + //------------------------------------------------------------------ + // Process::Notifications callback functions + //------------------------------------------------------------------ + static void + Initialize (void *baton, + lldb_private::Process *process); + + static void + ProcessStateChanged (void *baton, + lldb_private::Process *process, + lldb::StateType state); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (lldb_private::Thread &thread, + bool stop_others); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + void + PrivateInitialize (lldb_private::Process *process); + + void + PrivateProcessStateChanged (lldb_private::Process *process, + lldb::StateType state); + bool + LocateDYLD (); + + bool + DidSetNotificationBreakpoint () const; + + void + Clear (bool clear_process); + + void + PutToLog (lldb_private::Log *log) const; + + bool + ReadDYLDInfoFromMemoryAndSetNotificationCallback (lldb::addr_t addr); + + uint32_t + UpdateAllImageInfos (); + + static bool + NotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + void + UpdateAllImageInfosHeaderAndLoadCommands (); + + bool + UpdateCommPageLoadAddress (lldb_private::Module *module); + + uint32_t + AddrByteSize() + { + switch (m_dyld.header.magic) + { + case MH_MAGIC: + case MH_CIGAM: + return 4; + + case MH_MAGIC_64: + case MH_CIGAM_64: + return 8; + + default: + break; + } + return 0; + } + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic) + { + switch (magic) + { + case MH_MAGIC: + case MH_MAGIC_64: + return lldb::eByteOrderHost; + + case MH_CIGAM: + case MH_CIGAM_64: + if (lldb::eByteOrderHost == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; + } + + bool + ReadMachHeader (lldb::addr_t addr, + struct mach_header *header, + lldb_private::DataExtractor *load_command_data); + class Segment + { + public: + + Segment() : + name(), + addr(LLDB_INVALID_ADDRESS), + size(0) + { + } + + lldb_private::ConstString name; + lldb::addr_t addr; + lldb::addr_t size; + + bool + operator==(const Segment& rhs) const + { + return name == rhs.name && addr == rhs.addr && size == rhs.size; + } + + void + PutToLog (lldb_private::Log *log, + lldb::addr_t slide) const; + + }; + + struct DYLDImageInfo + { + lldb::addr_t address; // Address of mach header for this dylib + lldb::addr_t slide; // The amount to slide all segments by if there is a global slide. + lldb::addr_t mod_date; // Modification date for this dylib + lldb_private::FileSpec file_spec; // Resolved path for this dylib + lldb_private::UUID uuid; // UUID for this dylib if it has one, else all zeros + struct mach_header header; // The mach header for this image + std::vector segments; // All segment vmaddr and vmsize pairs for this executable (from memory of inferior) + + DYLDImageInfo() : + address(LLDB_INVALID_ADDRESS), + slide(0), + mod_date(0), + file_spec(), + uuid(), + header(), + segments() + { + } + + void + Clear(bool load_cmd_data_only) + { + if (!load_cmd_data_only) + { + address = LLDB_INVALID_ADDRESS; + slide = 0; + mod_date = 0; + file_spec.Clear(); + ::bzero (&header, sizeof(header)); + } + uuid.Clear(); + segments.clear(); + } + + bool + operator == (const DYLDImageInfo& rhs) const + { + return address == rhs.address + && slide == rhs.slide + && mod_date == rhs.mod_date + && file_spec == rhs.file_spec + && uuid == rhs.uuid + && memcmp(&header, &rhs.header, sizeof(header)) == 0 + && segments == rhs.segments; + } + + bool + UUIDValid() const + { + return uuid.IsValid(); + } + + const Segment * + FindSegment (const lldb_private::ConstString &name) const; + + void + PutToLog (lldb_private::Log *log) const; + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + struct DYLDAllImageInfos + { + uint32_t version; + uint32_t dylib_info_count; // Version >= 1 + lldb::addr_t dylib_info_addr; // Version >= 1 + lldb::addr_t notification; // Version >= 1 + bool processDetachedFromSharedRegion; // Version >= 1 + bool libSystemInitialized; // Version >= 2 + lldb::addr_t dyldImageLoadAddress; // Version >= 2 + + DYLDAllImageInfos() : + version (0), + dylib_info_count (0), + dylib_info_addr (LLDB_INVALID_ADDRESS), + notification (LLDB_INVALID_ADDRESS), + processDetachedFromSharedRegion (false), + libSystemInitialized (false), + dyldImageLoadAddress (LLDB_INVALID_ADDRESS) + { + } + + void + Clear() + { + version = 0; + dylib_info_count = 0; + dylib_info_addr = LLDB_INVALID_ADDRESS; + notification = LLDB_INVALID_ADDRESS; + processDetachedFromSharedRegion = false; + libSystemInitialized = false; + dyldImageLoadAddress = LLDB_INVALID_ADDRESS; + } + + bool + IsValid() const + { + return version >= 1 || version <= 6; + } + }; + + void + RegisterNotificationCallbacks(); + + void + UnregisterNotificationCallbacks(); + + uint32_t + ParseLoadCommands (const lldb_private::DataExtractor& data, + struct DYLDImageInfo& dylib_info, + lldb_private::FileSpec *lc_id_dylinker); + + bool + UpdateImageLoadAddress(lldb_private::Module *module, + struct DYLDImageInfo& info); + + bool + UnloadImageLoadAddress (lldb_private::Module *module, + struct DYLDImageInfo& info); + + bool + NeedToLocateDYLD () const; + + bool + SetNotificationBreakpoint (); + + bool + ReadAllImageInfosStructure (); + + DYLDImageInfo m_dyld; // Info about the curent dyld being used + lldb::addr_t m_dyld_all_image_infos_addr; + DYLDAllImageInfos m_dyld_all_image_infos; + lldb::user_id_t m_break_id; + DYLDImageInfo::collection m_dyld_image_infos; // Current shared libraries information + mutable lldb_private::Mutex m_mutex; + lldb_private::Process::Notifications m_notification_callbacks; + std::auto_ptr m_objc_trampoline_handler_ap; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLD_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp new file mode 100644 index 000000000000..946c8f9310c1 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp @@ -0,0 +1,72 @@ +//===-- DynamicLoaderMacOSXDYLDLog.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DynamicLoaderMacOSXDYLDLog.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +static Log * +LogAccessor (bool get, Log *log) +{ + static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. + if (get) + { +// // Debug code below for enabling logging by default +// if (g_log == NULL) +// { +// g_log = new Log("/dev/stdout", false); +// g_log->GetMask().SetAllFlagBits(0xffffffffu); +// g_log->GetOptions().Set(LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_THREAD_NAME); +// } + } + else + { + if (g_log) + delete g_log; + g_log = log; + } + + return g_log; +} + +Log * +DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +DynamicLoaderMacOSXDYLDLog::SetLog (Log *log) +{ + LogAccessor (false, log); +} + + +void +DynamicLoaderMacOSXDYLDLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h new file mode 100644 index 000000000000..9282ba576471 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h @@ -0,0 +1,34 @@ +//===-- DynamicLoaderMacOSXDYLDLog.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderMacOSXDYLDLog_h_ +#define liblldb_DynamicLoaderMacOSXDYLDLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/lldb-private.h" + +// Project includes + +class DynamicLoaderMacOSXDYLDLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet (uint32_t mask); + + static void + SetLog (lldb_private::Log *log); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLDLog_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp new file mode 100644 index 000000000000..169dc89c7915 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp @@ -0,0 +1,328 @@ +//===-- ObjCTrampolineHandler.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjCTrampolineHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/ExecutionContext.h" +#include "ThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +using namespace lldb; +using namespace lldb_private; + +const ObjCTrampolineHandler::DispatchFunction +ObjCTrampolineHandler::g_dispatch_functions[] = +{ + // NAME STRET SUPER FIXUP TYPE + {"objc_msgSend", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_stret", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_stret_fixup", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_stret_fixedup", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_fpret", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fpret_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fpret_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_fp2ret", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fp2ret_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fp2ret_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSendSuper", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper_stret", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2_fixup", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSendSuper2_fixedup", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSendSuper2_stret", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2_stret_fixup", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSendSuper2_stret_fixedup", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {NULL} +}; + +bool +ObjCTrampolineHandler::ModuleIsObjCLibrary (const ModuleSP &module_sp) +{ + const FileSpec &module_file_spec = module_sp->GetFileSpec(); + static ConstString ObjCName ("libobjc.A.dylib"); + + if (module_file_spec) + { + if (module_file_spec.GetFilename() == ObjCName) + return true; + } + + return false; +} + +ObjCTrampolineHandler::ObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module) : + m_process_sp (process_sp), + m_objc_module_sp (objc_module), + m_impl_fn_addr (LLDB_INVALID_ADDRESS), + m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS) +{ + // Look up the known resolution functions: + + ConstString get_impl_name("class_getMethodImplementation"); + ConstString get_impl_stret_name("class_getMethodImplementation_stret"); + + const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode); + const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode); + + if (class_getMethodImplementation) + m_impl_fn_addr = class_getMethodImplementation->GetValue().GetLoadAddress(m_process_sp.get()); + if (class_getMethodImplementation_stret) + m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetValue().GetLoadAddress(m_process_sp.get()); + + // FIXME: Do some kind of logging here. + if (m_impl_fn_addr == LLDB_INVALID_ADDRESS || m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) + return; + + // Look up the addresses for the objc dispatch functions and cache them. For now I'm inspecting the symbol + // names dynamically to figure out how to dispatch to them. If it becomes more complicated than this we can + // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map + // from there. + + for (int i = 0; g_dispatch_functions[i].name != NULL; i++) + { + ConstString name_const_str(g_dispatch_functions[i].name); + const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode); + if (msgSend_symbol) + { + // FixMe: Make g_dispatch_functions static table of DisptachFunctions, and have the map be address->index. + // Problem is we also need to lookup the dispatch function. For now we could have a side table of stret & non-stret + // dispatch functions. If that's as complex as it gets, we're fine. + + lldb::addr_t sym_addr = msgSend_symbol->GetValue().GetLoadAddress(m_process_sp.get()); + + m_msgSend_map.insert(std::pair(sym_addr, i)); + } + } +} + +ThreadPlanSP +ObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP ret_plan_sp; + lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + + MsgsendMap::iterator pos; + pos = m_msgSend_map.find (curr_pc); + if (pos != m_msgSend_map.end()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + const DispatchFunction *this_dispatch = &g_dispatch_functions[(*pos).second]; + + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + + Process *process = thread.CalculateProcess(); + const ABI *abi = process->GetABI(); + if (abi == NULL) + return ret_plan_sp; + + Target *target = thread.CalculateTarget(); + + // FIXME: Since neither the value nor the Clang QualType know their ASTContext, + // we have to make sure the type we put in our value list comes from the same ASTContext + // the ABI will use to get the argument values. THis is the bottom-most frame's module. + + ClangASTContext *clang_ast_context = target->GetScratchClangASTContext(); + ValueList argument_values; + Value input_value; + void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false); + input_value.SetValueType (Value::eValueTypeScalar); + input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type); + + int obj_index; + int sel_index; + + // If this is a struct return dispatch, then the first argument is the + // return struct pointer, and the object is the second, and the selector is the third. + // Otherwise the object is the first and the selector the second. + if (this_dispatch->stret_return) + { + obj_index = 1; + sel_index = 2; + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + } + else + { + obj_index = 0; + sel_index = 1; + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + } + + + bool success = abi->GetArgumentValues (thread, argument_values); + if (!success) + return ret_plan_sp; + + // Okay, the first value here is the object, we actually want the class of that object. + // For now we're just going with the ISA. + // FIXME: This should really be the return value of [object class] to properly handle KVO interposition. + + Value isa_value(*(argument_values.GetValueAtIndex(obj_index))); + + // This is a little cheesy, but since object->isa is the first field, + // making the object value a load address value and resolving it will get + // the pointer sized data pointed to by that value... + ExecutionContext exec_ctx; + thread.Calculate (exec_ctx); + + isa_value.SetValueType(Value::eValueTypeLoadAddress); + isa_value.ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); + + if (this_dispatch->fixedup == DispatchFunction::eFixUpFixed) + { + // For the FixedUp method the Selector is actually a pointer to a + // structure, the second field of which is the selector number. + Value *sel_value = argument_values.GetValueAtIndex(sel_index); + sel_value->GetScalar() += process->GetAddressByteSize(); + sel_value->SetValueType(Value::eValueTypeLoadAddress); + sel_value->ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); + } + else if (this_dispatch->fixedup == DispatchFunction::eFixUpToFix) + { + // FIXME: If the method dispatch is not "fixed up" then the selector is actually a + // pointer to the string name of the selector. We need to look that up... + // For now I'm going to punt on that and just return no plan. + if (log) + log->Printf ("Punting on stepping into un-fixed-up method dispatch."); + return ret_plan_sp; + } + + // FIXME: If this is a dispatch to the super-class, we need to get the super-class from + // the class, and disaptch to that instead. + // But for now I just punt and return no plan. + if (this_dispatch->is_super) + { + if (log) + log->Printf ("Punting on stepping into super method dispatch."); + return ret_plan_sp; + } + + ValueList dispatch_values; + dispatch_values.PushValue (isa_value); + dispatch_values.PushValue(*(argument_values.GetValueAtIndex(sel_index))); + + if (log) + { + log->Printf("Resolving method call for class - 0x%llx and selector - 0x%llx", + dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong()); + } + + lldb::addr_t impl_addr = LookupInCache (dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong()); + + if (impl_addr == LLDB_INVALID_ADDRESS) + { + + Address resolve_address(NULL, this_dispatch->stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr); + + StreamString errors; + { + // Scope for mutex locker: + Mutex::Locker (m_impl_function_mutex); + if (!m_impl_function.get()) + { + m_impl_function.reset(new ClangFunction(process->GetTargetTriple().GetCString(), + clang_ast_context, + clang_void_ptr_type, + resolve_address, + dispatch_values)); + + unsigned num_errors = m_impl_function->CompileFunction(errors); + if (num_errors) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Error compiling function: \"%s\".", errors.GetData()); + return ret_plan_sp; + } + + errors.Clear(); + if (!m_impl_function->WriteFunctionWrapper(exec_ctx, errors)) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Error Inserting function: \"%s\".", errors.GetData()); + return ret_plan_sp; + } + } + + } + + errors.Clear(); + + // Now write down the argument values for this call. + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + if (!m_impl_function->WriteFunctionArguments (exec_ctx, args_addr, resolve_address, dispatch_values, errors)) + return ret_plan_sp; + + ret_plan_sp.reset (new ThreadPlanStepThroughObjCTrampoline (thread, this, args_addr, + argument_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong(), + stop_others)); + } + else + { + if (log) + log->Printf ("Found implementation address in cache: 0x%llx", impl_addr); + + ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others)); + } + } + + return ret_plan_sp; +} + +void +ObjCTrampolineHandler::AddToCache (lldb::addr_t class_addr, lldb::addr_t selector, lldb::addr_t impl_addr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + log->Printf ("Caching: class 0x%llx selector 0x%llx implementation 0x%llx.", class_addr, selector, impl_addr); + } + m_impl_cache.insert (std::pair (ClassAndSel(class_addr, selector), impl_addr)); +} + +lldb::addr_t +ObjCTrampolineHandler::LookupInCache (lldb::addr_t class_addr, lldb::addr_t selector) +{ + MsgImplMap::iterator pos, end = m_impl_cache.end(); + pos = m_impl_cache.find (ClassAndSel(class_addr, selector)); + if (pos != end) + return (*pos).second; + return LLDB_INVALID_ADDRESS; +} + +ClangFunction * +ObjCTrampolineHandler::GetLookupImplementationWrapperFunction () +{ + return m_impl_function.get(); +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h new file mode 100644 index 000000000000..bc06d267d2f7 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h @@ -0,0 +1,133 @@ +//===-- ObjCTrampolineHandler.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_ObjCTrampolineHandler_h_ +#define lldb_ObjCTrampolineHandler_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Host/Mutex.h" + + +namespace lldb_private +{ +using namespace lldb; + +class ObjCTrampolineHandler { +public: + + ObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp); + + ~ObjCTrampolineHandler() {} + + static bool ModuleIsObjCLibrary (const ModuleSP &module_sp); + + ThreadPlanSP + GetStepThroughDispatchPlan (Thread &thread, bool stop_others); + + void + AddToCache (lldb::addr_t class_addr, lldb::addr_t sel, lldb::addr_t impl_addr); + + lldb::addr_t + LookupInCache (lldb::addr_t class_addr, lldb::addr_t sel); + + ClangFunction * + GetLookupImplementationWrapperFunction (); + + + struct DispatchFunction { + public: + typedef enum + { + eFixUpNone, + eFixUpFixed, + eFixUpToFix + } FixUpState; + + const char *name; + bool stret_return; + bool is_super; + FixUpState fixedup; + }; + +private: + static const DispatchFunction g_dispatch_functions[]; + + typedef std::map MsgsendMap; // This table maps an dispatch fn address to the index in g_dispatch_functions + MsgsendMap m_msgSend_map; + ProcessSP m_process_sp; + ModuleSP m_objc_module_sp; + lldb::addr_t get_impl_addr; + std::auto_ptr m_impl_function; + Mutex m_impl_function_mutex; + lldb::addr_t m_impl_fn_addr; + lldb::addr_t m_impl_stret_fn_addr; + + + // 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; + MsgImplMap m_impl_cache; + +}; + +}; // using namespace lldb_private + +#endif // lldb_ObjCTrampolineHandler_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp new file mode 100644 index 000000000000..a0cb0a86f18b --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp @@ -0,0 +1,151 @@ +//===-- ThreadPlanStepThroughObjCTrampoline.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 "ThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepThroughObjCTrampoline constructor +//---------------------------------------------------------------------- +ThreadPlanStepThroughObjCTrampoline::ThreadPlanStepThroughObjCTrampoline( + Thread &thread, + ObjCTrampolineHandler *trampoline_handler, + lldb::addr_t args_addr, + lldb::addr_t object_ptr, + lldb::addr_t class_ptr, + lldb::addr_t sel_ptr, + bool stop_others) : + ThreadPlan ("MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion, eVoteNoOpinion), + m_objc_trampoline_handler (trampoline_handler), + m_impl_function (trampoline_handler->GetLookupImplementationWrapperFunction()), + m_args_addr (args_addr), + m_object_ptr (object_ptr), + m_class_ptr (class_ptr), + m_sel_ptr (sel_ptr), + m_stop_others (stop_others) +{ + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlanStepThroughObjCTrampoline::~ThreadPlanStepThroughObjCTrampoline() +{ +} + +void +ThreadPlanStepThroughObjCTrampoline::DidPush () +{ + StreamString errors; + ExecutionContext exc_context; + m_thread.Calculate(exc_context); + m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_context, m_args_addr, errors, m_stop_others)); + m_func_sp->SetPrivate(true); + m_thread.QueueThreadPlan (m_func_sp, false); +} + +void +ThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s, + lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("Step through ObjC trampoline"); + else + { + s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx class: 0x%llx selector: 0x%llx", + m_object_ptr, m_class_ptr, m_sel_ptr); + } +} + +bool +ThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepThroughObjCTrampoline::PlanExplainsStop () +{ + // This plan should actually never stop when it is on the top of the plan + // stack, since it does all it's running in client plans. + return false; +} + +lldb::StateType +ThreadPlanStepThroughObjCTrampoline::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr) +{ + if (m_func_sp.get() == NULL || m_thread.IsThreadPlanDone(m_func_sp.get())) + { + m_func_sp.reset(); + if (!m_run_to_sp) + { + Value target_addr_value; + ExecutionContext exc_context; + m_thread.Calculate(exc_context); + m_impl_function->FetchFunctionResults (exc_context, m_args_addr, target_addr_value); + m_impl_function->DeallocateFunctionResults(exc_context, m_args_addr); + lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); + Address target_address(NULL, target_addr); + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Running to ObjC method implementation: 0x%llx", target_addr); + + m_objc_trampoline_handler->AddToCache (m_class_ptr, m_sel_ptr, target_addr); + + // Extract the target address from the value: + + m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_address, m_stop_others)); + m_thread.QueueThreadPlan(m_run_to_sp, false); + m_run_to_sp->SetPrivate(true); + return false; + } + else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) + { + SetPlanComplete(); + return true; + } + } + return false; +} + +// The base class MischiefManaged does some cleanup - so you have to call it +// in your MischiefManaged derived class. +bool +ThreadPlanStepThroughObjCTrampoline::MischiefManaged () +{ + if (IsPlanComplete()) + return true; + else + return false; +} + +bool +ThreadPlanStepThroughObjCTrampoline::WillStop() +{ + return true; +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h new file mode 100644 index 000000000000..8033718277e0 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanStepThroughObjCTrampoline.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_ThreadPlanStepThroughObjCTrampoline_h_ +#define lldb_ThreadPlanStepThroughObjCTrampoline_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb-types.h" +#include "lldb-enumerations.h" +#include "lldb/Target/ThreadPlan.h" +#include "ObjCTrampolineHandler.h" + +namespace lldb_private +{ + +class ThreadPlanStepThroughObjCTrampoline : public ThreadPlan +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlanStepThroughObjCTrampoline(Thread &thread, + ObjCTrampolineHandler *trampoline_handler, + lldb::addr_t args_addr, + lldb::addr_t object_ptr, + lldb::addr_t class_ptr, + lldb::addr_t sel_ptr, + bool stop_others); + + virtual ~ThreadPlanStepThroughObjCTrampoline(); + + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + PlanExplainsStop (); + + + virtual lldb::StateType + RunState (); + + virtual bool + ShouldStop (Event *event_ptr); + + // The base class MischiefManaged does some cleanup - so you have to call it + // in your MischiefManaged derived class. + virtual bool + MischiefManaged (); + + virtual void + DidPush(); + + virtual bool + WillStop(); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlanStepThroughObjCTrampoline can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For ThreadPlanStepThroughObjCTrampoline only + //------------------------------------------------------------------ + ThreadPlanSP m_func_sp; // This is the function call plan. We fill it at start, then set it + // to NULL when this plan is done. That way we know to go to: + lldb::addr_t m_args_addr; // Stores the address for our step through function result structure. + ThreadPlanSP m_run_to_sp; // The plan that runs to the target. + bool m_stop_others; + ObjCTrampolineHandler *m_objc_trampoline_handler; + ClangFunction *m_impl_function; // This is a pointer to a impl function that + // is owned by the client that pushes this plan. + lldb::addr_t m_object_ptr; + lldb::addr_t m_class_ptr; + lldb::addr_t m_sel_ptr; +}; + +}; // namespace lldb_private +#endif // lldb_ThreadPlanStepThroughObjCTrampoline_h_ diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp new file mode 100644 index 000000000000..c2fb2a552b3c --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -0,0 +1,428 @@ +//===-- ObjectContainerBSDArchive.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerBSDArchive.h" + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + + + +ObjectContainerBSDArchive::Object::Object() : + ar_name(), + ar_date(0), + ar_uid(0), + ar_gid(0), + ar_mode(0), + ar_size(0), + ar_file_offset(0), + ar_file_size(0) +{ +} + +void +ObjectContainerBSDArchive::Object::Clear() +{ + ar_name.Clear(); + ar_date = 0; + ar_uid = 0; + ar_gid = 0; + ar_mode = 0; + ar_size = 0; + ar_file_offset = 0; + ar_file_size = 0; +} + +uint32_t +ObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, uint32_t offset) +{ + size_t ar_name_len = 0; + std::string str; + char *err; + str.assign ((const char *)data.GetData(&offset, 16), 16); + if (str.find(AR_EFMT1) == 0) + { + // If the name is longer than 16 bytes, or contains an embedded space + // then it will use this format where the length of the name is + // here and the name characters are after this header. + ar_name_len = strtoul(str.c_str() + 3, &err, 10); + } + else + { + // Strip off any spaces (if the object file name contains spaces it + // will use the extended format above). + str.erase (str.find(' ')); + ar_name.SetCString(str.c_str()); + } + + str.assign ((const char *)data.GetData(&offset, 12), 12); + ar_date = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 6), 6); + ar_uid = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 6), 6); + ar_gid = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 8), 8); + ar_mode = strtoul(str.c_str(), &err, 8); + + str.assign ((const char *)data.GetData(&offset, 10), 10); + ar_size = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) + { + if (ar_name_len > 0) + { + str.assign ((const char *)data.GetData(&offset, ar_name_len), ar_name_len); + ar_name.SetCString (str.c_str()); + } + ar_file_offset = offset; + ar_file_size = ar_size - ar_name_len; + return offset; + } + return LLDB_INVALID_INDEX32; +} + +ObjectContainerBSDArchive::Archive::Archive +( + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &time +) : + m_arch (arch), + m_time (time), + m_objects() +{ +} + +ObjectContainerBSDArchive::Archive::~Archive () +{ +} + +size_t +ObjectContainerBSDArchive::Archive::ParseObjects (DataExtractor &data) +{ + std::string str; + uint32_t offset = 0; + str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG); + if (str == ARMAG) + { + Object obj; + do + { + offset = obj.Extract (data, offset); + if (offset == LLDB_INVALID_INDEX32) + break; + uint32_t obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append (obj.ar_name.GetCString(), obj_idx); + offset += obj.ar_file_size; + obj.Clear(); + } while (data.ValidOffset(offset)); + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort (); + } + return m_objects.size(); +} + +ObjectContainerBSDArchive::Object * +ObjectContainerBSDArchive::Archive::FindObject (const ConstString &object_name) +{ + const UniqueCStringMap::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString()); + if (match) + return &m_objects[match->value]; + return NULL; +} + + +ObjectContainerBSDArchive::Archive::shared_ptr +ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time) +{ + Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); + shared_ptr archive_sp; + Archive::Map &archive_map = Archive::GetArchiveCache (); + Archive::Map::iterator pos; + for (pos = archive_map.find (file); pos != archive_map.end() && pos->first == file; ++pos) + { + if (pos->second->GetArchitecture() == arch && + pos->second->GetModificationTime() == time) + { + archive_sp = pos->second; + } + } + return archive_sp; +} + +ObjectContainerBSDArchive::Archive::shared_ptr +ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile +( + const FileSpec &file, + const ArchSpec &arch, + const TimeValue &time, + DataExtractor &data +) +{ + shared_ptr archive_sp(new Archive (arch, time)); + if (archive_sp) + { + if (archive_sp->ParseObjects (data) > 0) + { + Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); + Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); + } + else + { + archive_sp.reset(); + } + } + return archive_sp; +} + +ObjectContainerBSDArchive::Archive::Map & +ObjectContainerBSDArchive::Archive::GetArchiveCache () +{ + static Archive::Map g_archive_map; + return g_archive_map; +} + +Mutex & +ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () +{ + static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); + return g_archive_map_mutex; +} + + +void +ObjectContainerBSDArchive::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectContainerBSDArchive::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectContainerBSDArchive::GetPluginNameStatic() +{ + return "object-container.bsd-archive"; +} + +const char * +ObjectContainerBSDArchive::GetPluginDescriptionStatic() +{ + return "BSD Archive object container reader."; +} + + +ObjectContainer * +ObjectContainerBSDArchive::CreateInstance +( + Module* module, + DataBufferSP& dataSP, + const FileSpec *file, + addr_t offset, + addr_t length) +{ + if (file) + { + std::string object; + + Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, module->GetArchitecture(), module->GetModificationTime())); + + if (archive_sp) + { + // We already have this archive in our cache, use it + std::auto_ptr container_ap(new ObjectContainerBSDArchive (module, dataSP, file, offset, length)); + if (container_ap.get()) + { + container_ap->SetArchive (archive_sp); + return container_ap.release(); + } + } + + if (dataSP) + { + if (ObjectContainerBSDArchive::MagicBytesMatch(dataSP)) + { + // Read everything since we need that in order to index all the + // objects in the archive + dataSP = file->ReadFileContents(offset, length); + + std::auto_ptr container_ap(new ObjectContainerBSDArchive (module, dataSP, file, offset, length)); + if (container_ap->ParseHeader()) + return container_ap.release(); + } + } + } + return NULL; +} + + + +bool +ObjectContainerBSDArchive::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); + if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) + { + armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; + if (strncmp(armag, ARFMAG, 2) == 0) + return true; + } + return false; +} + +ObjectContainerBSDArchive::ObjectContainerBSDArchive +( + Module* module, + DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t size +) : + ObjectContainer (module, file, offset, size, dataSP), + m_archive_sp () +{ +} +void +ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) +{ + m_archive_sp = archive_sp; +} + + + +ObjectContainerBSDArchive::~ObjectContainerBSDArchive() +{ +} + +bool +ObjectContainerBSDArchive::ParseHeader () +{ + if (m_archive_sp.get() == NULL) + { + if (m_data.GetByteSize() > 0) + { + m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, + m_module->GetArchitecture(), + m_module->GetModificationTime(), + m_data); + // The archive might be huge, so clear "m_data" to free up the + // memory since it will contain the entire file (possibly more than + // one architecture slice). We already have an index of all objects + // in the file, so we will be ready to serve up those objects. + m_data.Clear(); + } + } + return m_archive_sp.get() != NULL; +} + +void +ObjectContainerBSDArchive::Dump (Stream *s) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerBSDArchive, num_archs = %u, num_objects = %u", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; iIndent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", arch.AsCString()); + } + for (i=0; iIndent(); + s->Printf("object[%u] = %s\n", GetObjectNameAtIndex (i)); + } + s->IndentLess(); + s->EOL(); +} + +ObjectFile * +ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) +{ + if (m_module->GetObjectName() && m_archive_sp) + { + Object *object = m_archive_sp->FindObject (m_module->GetObjectName()); + if (object) + return ObjectFile::FindPlugin (m_module, file, m_offset + object->ar_file_offset, object->ar_file_size); + } + return NULL; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectContainerBSDArchive::GetPluginName() +{ + return "object-container.bsd-archive"; +} + +const char * +ObjectContainerBSDArchive::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerBSDArchive::GetPluginVersion() +{ + return 1; +} + +void +ObjectContainerBSDArchive::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectContainerBSDArchive::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectContainerBSDArchive::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h new file mode 100644 index 000000000000..88aba019ead4 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h @@ -0,0 +1,183 @@ +//===-- ObjectContainerBSDArchive.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectContainerBSDArchive_h_ +#define liblldb_ObjectContainerBSDArchive_h_ + +#include "lldb/Symbol/ObjectContainer.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Host/TimeValue.h" + +class ObjectContainerBSDArchive : + public lldb_private::ObjectContainer +{ +public: + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectContainerBSDArchive (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectContainerBSDArchive(); + + virtual bool + ParseHeader (); + + virtual void + Dump (lldb_private::Stream *s) const; + + virtual lldb_private::ObjectFile * + GetObjectFile (const lldb_private::FileSpec *file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + + struct Object + { + Object(); + + void + Clear(); + + uint32_t + Extract (const lldb_private::DataExtractor& data, uint32_t offset); + + lldb_private::ConstString ar_name; // name + uint32_t ar_date; // modification time + uint16_t ar_uid; // user id + uint16_t ar_gid; // group id + uint16_t ar_mode; // octal file permissions + uint32_t ar_size; // size in bytes + uint32_t ar_file_offset; // file offset in bytes from the beginning of the file of the object data + uint32_t ar_file_size; // length of the object data + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + class Archive + { + public: + typedef lldb::SharedPtr::Type shared_ptr; + typedef std::multimap Map; + + static Map & + GetArchiveCache (); + + static lldb_private::Mutex & + GetArchiveCacheMutex (); + + static Archive::shared_ptr + FindCachedArchive (const lldb_private::FileSpec &file, + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time); + + static Archive::shared_ptr + ParseAndCacheArchiveForFile (const lldb_private::FileSpec &file, + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time, + lldb_private::DataExtractor &data); + + Archive (const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time); + + ~Archive (); + + size_t + ParseObjects (lldb_private::DataExtractor &data); + + Object * + FindObject (const lldb_private::ConstString &object_name); + + const lldb_private::TimeValue & + GetModificationTime() + { + return m_time; + } + + const lldb_private::ArchSpec & + GetArchitecture () + { + return m_arch; + } + + protected: + + //---------------------------------------------------------------------- + // Member Variables + //---------------------------------------------------------------------- + lldb_private::ArchSpec m_arch; + lldb_private::TimeValue m_time; + Object::collection m_objects; + lldb_private::UniqueCStringMap m_object_name_to_index_map; + }; + + void + SetArchive (Archive::shared_ptr &archive_sp); + + Archive::shared_ptr m_archive_sp; +}; + +#endif // liblldb_ObjectContainerBSDArchive_h_ diff --git a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp new file mode 100644 index 000000000000..015e498a7ccd --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp @@ -0,0 +1,259 @@ +//===-- ObjectContainerUniversalMachO.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerUniversalMachO.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + + +void +ObjectContainerUniversalMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectContainerUniversalMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectContainerUniversalMachO::GetPluginNameStatic() +{ + return "object-container.mach-o"; +} + +const char * +ObjectContainerUniversalMachO::GetPluginDescriptionStatic() +{ + return "Universal mach-o object container reader."; +} + + +ObjectContainer * +ObjectContainerUniversalMachO::CreateInstance +( + Module* module, + DataBufferSP& dataSP, + const FileSpec *file, + addr_t offset, + addr_t length +) +{ + if (ObjectContainerUniversalMachO::MagicBytesMatch(dataSP)) + { + std::auto_ptr container_ap(new ObjectContainerUniversalMachO (module, dataSP, file, offset, length)); + if (container_ap->ParseHeader()) + { + return container_ap.release(); + } + } + return NULL; +} + + + +bool +ObjectContainerUniversalMachO::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return magic == FAT_MAGIC || magic == FAT_CIGAM; +} + +ObjectContainerUniversalMachO::ObjectContainerUniversalMachO +( + Module* module, + DataBufferSP& dataSP, + const FileSpec *file, + addr_t offset, + addr_t length +) : + ObjectContainer (module, file, offset, length, dataSP), + m_header(), + m_fat_archs() +{ + memset(&m_header, 0, sizeof(m_header)); +} + + +ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() +{ +} + +bool +ObjectContainerUniversalMachO::ParseHeader () +{ + // Store the file offset for this universal file as we could have a universal .o file + // in a BSD archive, or be contained in another kind of object. + uint32_t offset = 0; + // Universal mach-o files always have their headers in big endian. + m_data.SetByteOrder (eByteOrderBig); + m_header.magic = m_data.GetU32(&offset); + + if (m_header.magic == FAT_MAGIC) + { + m_data.SetAddressByteSize(4); + + m_header.nfat_arch = m_data.GetU32(&offset); + + const size_t nfat_arch_size = sizeof(fat_arch_t) * m_header.nfat_arch; + // See if the current data we have is enough for all of the fat headers? + if (!m_data.ValidOffsetForDataOfSize(offset, nfat_arch_size)) + { + // The fat headers are larger than the number of bytes we have been + // given when this class was constructed. We will read the exact number + // of bytes that we need. + DataBufferSP data_sp(m_file.ReadFileContents(m_offset, nfat_arch_size + sizeof(fat_header_t))); + m_data.SetData (data_sp); + } + + // Now we should have enough data for all of the fat headers, so lets index + // them so we know how many architectures that this univeral binary contains. + uint32_t arch_idx = 0; + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (m_data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch_t))) + { + fat_arch_t arch; + if (m_data.GetU32(&offset, &arch, sizeof(fat_arch_t)/sizeof(uint32_t))) + { + m_fat_archs.push_back(arch); + } + } + } + // Now that we have indexed the universal headers, we no longer need any cached data. + m_data.Clear(); + + return true; + } + else + { + memset(&m_header, 0, sizeof(m_header)); + } + + return false; +} + +void +ObjectContainerUniversalMachO::Dump (Stream *s) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerUniversalMachO, num_archs = %u, num_objects = %u", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; iIndent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", arch.AsCString()); + } + for (i=0; iIndent(); + s->Printf("object[%u] = %s\n", GetObjectNameAtIndex (i)); + } + s->IndentLess(); + s->EOL(); +} + +size_t +ObjectContainerUniversalMachO::GetNumArchitectures () const +{ + return m_header.nfat_arch; +} + +bool +ObjectContainerUniversalMachO::GetArchitectureAtIndex (uint32_t idx, ArchSpec& arch) const +{ + if (idx < m_header.nfat_arch) + { + arch.SetArch(m_fat_archs[idx].cputype, m_fat_archs[idx].cpusubtype); + return true; + } + return false; +} + +ObjectFile * +ObjectContainerUniversalMachO::GetObjectFile (const FileSpec *file) +{ + uint32_t arch_idx = 0; + const ArchSpec arch = m_module->GetArchitecture(); + ArchSpec curr_arch; + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (GetArchitectureAtIndex (arch_idx, curr_arch)) + { + if (arch == curr_arch) + { + return ObjectFile::FindPlugin (m_module, file, m_offset + m_fat_archs[arch_idx].offset, m_fat_archs[arch_idx].size); + } + } + } + return NULL; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectContainerUniversalMachO::GetPluginName() +{ + return "ObjectContainerUniversalMachO"; +} + +const char * +ObjectContainerUniversalMachO::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerUniversalMachO::GetPluginVersion() +{ + return 1; +} + +void +ObjectContainerUniversalMachO::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectContainerUniversalMachO::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectContainerUniversalMachO::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h new file mode 100644 index 000000000000..8a7f975bc67b --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h @@ -0,0 +1,103 @@ +//===-- ObjectContainerUniversalMachO.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectContainerUniversalMachO_h_ +#define liblldb_ObjectContainerUniversalMachO_h_ + +#include + +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Core/FileSpec.h" + +class ObjectContainerUniversalMachO : + public lldb_private::ObjectContainer +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectContainerUniversalMachO (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectContainerUniversalMachO(); + + virtual bool + ParseHeader (); + + virtual void + Dump (lldb_private::Stream *s) const; + + virtual size_t + GetNumArchitectures () const; + + virtual bool + GetArchitectureAtIndex (uint32_t cpu_idx, lldb_private::ArchSpec& arch) const; + + virtual lldb_private::ObjectFile * + GetObjectFile (const lldb_private::FileSpec *file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + typedef struct fat_header fat_header_t; + typedef struct fat_arch fat_arch_t; + fat_header_t m_header; + std::vector m_fat_archs; +}; + +#endif // liblldb_ObjectContainerUniversalMachO_h_ diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp new file mode 100644 index 000000000000..b34dd3db289a --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -0,0 +1,929 @@ +//===-- ObjectFileELF.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileELF.h" + +#include +#include + +#include + +#include +#include "elf.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" + +#define CASE_AND_STREAM(s, def, width) case def: s->Printf("%-*s", width, #def); break; + +static uint32_t ELFMachineToMachCPU(Elf32_Half machine); + +using namespace lldb; +using namespace lldb_private; +using namespace std; + + +#include +#include + + +void +ObjectFileELF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectFileELF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectFileELF::GetPluginNameStatic() +{ + return "object-file.elf32"; +} + +const char * +ObjectFileELF::GetPluginDescriptionStatic() +{ + return "ELF object file reader (32-bit)."; +} + + +ObjectFile * +ObjectFileELF::CreateInstance (Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) +{ + if (ObjectFileELF::MagicBytesMatch(dataSP)) + { + std::auto_ptr objfile_ap(new ObjectFileELF (module, dataSP, file, offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +bool +ObjectFileELF::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + const uint8_t* magic = data.PeekData(0, 4); + if (magic != NULL) + { + return magic[EI_MAG0] == 0x7f + && magic[EI_MAG1] == 'E' + && magic[EI_MAG2] == 'L' + && magic[EI_MAG3] == 'F'; + } + return false; +} + + +ObjectFileELF::ObjectFileELF(Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) : + ObjectFile (module, file, offset, length, dataSP), + m_header(), + m_program_headers(), + m_section_headers(), + m_sections_ap(), + m_symtab_ap(), + m_shstr_data() +{ + if (file) + m_file = *file; + ::bzero (&m_header, sizeof(m_header)); +} + + +ObjectFileELF::~ObjectFileELF() +{ +} + +ByteOrder +ObjectFileELF::GetByteOrder () const +{ + if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) + return eByteOrderBig; + if (m_header.e_ident[EI_DATA] == ELFDATA2LSB) + return eByteOrderLittle; + return eByteOrderInvalid; +} + +size_t +ObjectFileELF::GetAddressByteSize () const +{ + return m_data.GetAddressByteSize(); +} + +bool +ObjectFileELF::ParseHeader () +{ + m_data.SetAddressByteSize(4); + uint32_t offset = GetOffset(); + if (m_data.GetU8(&offset, m_header.e_ident, EI_NIDENT) == NULL) + return false; + + m_data.SetByteOrder(GetByteOrder()); + + // Read e_type and e_machine + if (m_data.GetU16(&offset, &m_header.e_type, 2) == NULL) + return false; + + // read e_version, e_entry, e_phoff, e_shoff, e_flags + if (m_data.GetU32(&offset, &m_header.e_version, 5) == NULL) + return false; + + // Read e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx + if (m_data.GetU16(&offset, &m_header.e_ehsize, 6) == NULL) + return false; + + return true; +} + +bool +ObjectFileELF::GetUUID (UUID* uuid) +{ + return false; +} + +uint32_t +ObjectFileELF::GetDependentModules(FileSpecList& files) +{ + return 0; +} + +//---------------------------------------------------------------------- +// ParseProgramHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseProgramHeaders() +{ + // We have already parsed the program headers + if (!m_program_headers.empty()) + return m_program_headers.size(); + + uint32_t offset = 0; + if (m_header.e_phnum > 0) + { + m_program_headers.resize(m_header.e_phnum); + + if (m_program_headers.size() != m_header.e_phnum) + return 0; + + const size_t byte_size = m_header.e_phnum * m_header.e_phentsize; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_header.e_phoff, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + DataExtractor data(buffer_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + uint32_t idx; + for (idx = 0; idx < m_header.e_phnum; ++idx) + { + if (data.GetU32(&offset, &m_program_headers[idx].p_type, 8) == NULL) + return 0; + } + if (idx < m_program_headers.size()) + m_program_headers.resize(idx); + } + + return m_program_headers.size(); +} + + +//---------------------------------------------------------------------- +// ParseSectionHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseSectionHeaders() +{ + // We have already parsed the section headers + if (!m_section_headers.empty()) + return m_section_headers.size(); + + if (m_header.e_shnum > 0) + { + uint32_t offset = 0; + + m_section_headers.resize(m_header.e_shnum); + + if (m_section_headers.size() != m_header.e_shnum) + return 0; + + const size_t byte_size = m_header.e_shnum * m_header.e_shentsize; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_header.e_shoff, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + DataExtractor data(buffer_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + uint32_t idx; + for (idx = 0; idx < m_header.e_shnum; ++idx) + { + if (data.GetU32(&offset, &m_section_headers[idx].sh_name, 10) == NULL) + break; + } + if (idx < m_section_headers.size()) + m_section_headers.resize(idx); + } + + return m_section_headers.size(); +} + +size_t +ObjectFileELF::GetSectionHeaderStringTable() +{ + if (m_shstr_data.GetByteSize() == 0) + { + if (m_header.e_shstrndx && m_header.e_shstrndx < m_section_headers.size()) + { + const size_t byte_size = m_section_headers[m_header.e_shstrndx].sh_size; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_section_headers[m_header.e_shstrndx].sh_offset, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + m_shstr_data.SetData(buffer_sp); + } + } + return m_shstr_data.GetByteSize(); +} + +uint32_t +ObjectFileELF::GetSectionIndexByName(const char *name) +{ + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t offset = 1; // Skip leading NULL string at offset 0; + while (m_shstr_data.ValidOffset(offset)) + { + uint32_t sh_name = offset; // Save offset in case we find a match + const char* sectionHeaderName = m_shstr_data.GetCStr(&offset); + if (sectionHeaderName) + { + if (strcmp(name, sectionHeaderName) == 0) + { + SectionHeaderCollIter pos; + for (pos = m_section_headers.begin(); pos != m_section_headers.end(); ++pos) + { + if ( (*pos).sh_name == sh_name ) + { + // section indexes are 1 based + return std::distance(m_section_headers.begin(), pos) + 1; + } + } + return UINT32_MAX; + } + } + else + { + return UINT32_MAX; + } + } + } + + return UINT32_MAX; +} + +SectionList * +ObjectFileELF::GetSectionList() +{ + if (m_sections_ap.get() == NULL) + { + m_sections_ap.reset(new SectionList()); + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t sh_idx = 0; + const size_t num_sections = m_section_headers.size(); + for (sh_idx = 0; sh_idx < num_sections; ++sh_idx) + { + ConstString section_name(m_shstr_data.PeekCStr(m_section_headers[sh_idx].sh_name)); + uint64_t section_file_size = m_section_headers[sh_idx].sh_type == SHT_NOBITS ? 0 : m_section_headers[sh_idx].sh_size; + SectionSP section_sp(new Section(NULL, // Parent section + GetModule(), // Module to which this section belongs + sh_idx + 1, // Section ID is the 1 based + section_name, // Name of this section + eSectionTypeOther, // TODO: fill this in appropriately for ELF... + m_section_headers[sh_idx].sh_addr, // File VM address + m_section_headers[sh_idx].sh_size, // VM size in bytes of this section + m_section_headers[sh_idx].sh_offset, // Offset to the data for this section in the file + section_file_size, // Size in bytes of this section as found in the the file + m_section_headers[sh_idx].sh_flags)); // Flags for this section + if (section_sp.get()) + m_sections_ap->AddSection(section_sp); + + } + } + } + return m_sections_ap.get(); +} + +static void +ParseSymbols (Symtab *symtab, SectionList *section_list, const Elf32_Shdr &symtab_shdr, const DataExtractor& symtab_data, const DataExtractor& strtab_data) +{ + assert (sizeof(Elf32_Sym) == symtab_shdr.sh_entsize); + const uint32_t num_symbols = symtab_data.GetByteSize() / sizeof(Elf32_Sym); + uint32_t offset = 0; + Elf32_Sym symbol; + uint32_t i; + static ConstString text_section_name(".text"); + static ConstString init_section_name(".init"); + static ConstString fini_section_name(".fini"); + static ConstString ctors_section_name(".ctors"); + static ConstString dtors_section_name(".dtors"); + + static ConstString data_section_name(".data"); + static ConstString rodata_section_name(".rodata"); + static ConstString rodata1_section_name(".rodata1"); + static ConstString data2_section_name(".data1"); + static ConstString bss_section_name(".bss"); + + for (i=0; iGetSectionAtIndex (symbol.st_shndx).get(); + break; + } + + switch (ELF32_ST_BIND (symbol.st_info)) + { + default: + case STT_NOTYPE: + // The symbol's type is not specified. + break; + + case STT_OBJECT: + // The symbol is associated with a data object, such as a variable, an array, etc. + symbol_type == eSymbolTypeData; + break; + + case STT_FUNC: + // The symbol is associated with a function or other executable code. + symbol_type == eSymbolTypeCode; + break; + + case STT_SECTION: + // The symbol is associated with a section. Symbol table entries of + // this type exist primarily for relocation and normally have + // STB_LOCAL binding. + break; + + case STT_FILE: + // Conventionally, the symbol's name gives the name of the source + // file associated with the object file. A file symbol has STB_LOCAL + // binding, its section index is SHN_ABS, and it precedes the other + // STB_LOCAL symbols for the file, if it is present. + symbol_type == eSymbolTypeObjectFile; + break; + } + + if (symbol_type == eSymbolTypeInvalid) + { + if (symbol_section) + { + const ConstString §_name = symbol_section->GetName(); + if (sect_name == text_section_name || + sect_name == init_section_name || + sect_name == fini_section_name || + sect_name == ctors_section_name || + sect_name == dtors_section_name) + { + symbol_type = eSymbolTypeCode; + } + else + if (sect_name == data_section_name || + sect_name == data2_section_name || + sect_name == rodata_section_name || + sect_name == rodata1_section_name || + sect_name == bss_section_name) + { + symbol_type = eSymbolTypeData; + } + } + } + + uint64_t symbol_value = symbol.st_value; + if (symbol_section != NULL) + symbol_value -= symbol_section->GetFileAddress(); + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); + + Symbol dc_symbol(i, // ID is the original symbol table index + symbol_name, // symbol name + false, // Is the symbol name mangled? + symbol_type, // type of this symbol + ELF32_ST_BIND (symbol.st_info) == STB_GLOBAL, // Is this globally visible? + false, // Is this symbol debug info? + false, // Is this symbol a trampoline? + false, // Is this symbol artificial? + symbol_section, // section pointer if symbol_value is an offset within a section, else NULL + symbol_value, // offset from section if section is non-NULL, else the value for this symbol + symbol.st_size, // size in bytes of this symbol + symbol.st_other << 8 | symbol.st_info); // symbol flags + symtab->AddSymbol(dc_symbol); + } +} + + +Symtab * +ObjectFileELF::GetSymtab() +{ + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t symtab_idx = UINT32_MAX; + uint32_t dynsym_idx = UINT32_MAX; + uint32_t sh_idx = 0; + const size_t num_sections = m_section_headers.size(); + for (sh_idx = 0; sh_idx < num_sections; ++sh_idx) + { + if (m_section_headers[sh_idx].sh_type == SHT_SYMTAB) + { + symtab_idx = sh_idx; + break; + } + if (m_section_headers[sh_idx].sh_type == SHT_DYNSYM) + { + dynsym_idx = sh_idx; + } + } + + SectionList *section_list = NULL; + static ConstString g_symtab(".symtab"); + static ConstString g_strtab(".strtab"); + static ConstString g_dynsym(".dynsym"); + static ConstString g_dynstr(".dynstr"); + // Check if we found a full symbol table? + if (symtab_idx < num_sections) + { + section_list = GetSectionList(); + if (section_list) + { + Section *symtab_section = section_list->FindSectionByName(g_symtab).get(); + Section *strtab_section = section_list->FindSectionByName(g_strtab).get(); + if (symtab_section && strtab_section) + { + DataExtractor symtab_data; + DataExtractor strtab_data; + if (symtab_section->ReadSectionDataFromObjectFile (this, symtab_data) > 0 && + strtab_section->ReadSectionDataFromObjectFile (this, strtab_data) > 0) + { + ParseSymbols (m_symtab_ap.get(), section_list, m_section_headers[symtab_idx], symtab_data, strtab_data); + } + } + } + } + // Check if we found a reduced symbol table that gets used for dynamic linking? + else if (dynsym_idx < num_sections) + { + section_list = GetSectionList(); + if (section_list) + { + Section *dynsym_section = section_list->FindSectionByName(g_dynsym).get(); + Section *dynstr_section = section_list->FindSectionByName(g_dynstr).get(); + if (dynsym_section && dynstr_section) + { + DataExtractor dynsym_data; + DataExtractor dynstr_data; + if (dynsym_section->ReadSectionDataFromObjectFile (this, dynsym_data) > 0 && + dynstr_section->ReadSectionDataFromObjectFile (this, dynstr_data) > 0) + { + ParseSymbols (m_symtab_ap.get(), section_list, m_section_headers[dynsym_idx], dynsym_data, dynstr_data); + } + } + } + } + } + } + return m_symtab_ap.get(); +} + +// +////---------------------------------------------------------------------- +//// GetNListSymtab +////---------------------------------------------------------------------- +//bool +//ELF32RuntimeFileParser::GetNListSymtab(BinaryDataRef& stabs_data, BinaryDataRef& stabstr_data, bool locals_only, uint32_t& value_size) +//{ +// value_size = 4; // Size in bytes of the nlist n_value member +// return GetSectionInfo(GetSectionIndexByName(".stab"), NULL, NULL, NULL, NULL, NULL, NULL, &stabs_data, NULL) && +// GetSectionInfo(GetSectionIndexByName(".stabstr"), NULL, NULL, NULL, NULL, NULL, NULL, &stabstr_data, NULL); +//} +// +//===----------------------------------------------------------------------===// +// Dump +// +// Dump the specifics of the runtime file container (such as any headers +// segments, sections, etc). +//---------------------------------------------------------------------- +void +ObjectFileELF::Dump(Stream *s) +{ + DumpELFHeader(s, m_header); + s->EOL(); + DumpELFProgramHeaders(s); + s->EOL(); + DumpELFSectionHeaders(s); + s->EOL(); + SectionList *section_list = GetSectionList(); + if (section_list) + section_list->Dump(s, NULL, true); + Symtab *symtab = GetSymtab(); + if (symtab) + symtab->Dump(s, NULL); + s->EOL(); +} + +//---------------------------------------------------------------------- +// DumpELFHeader +// +// Dump the ELF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader(Stream *s, const Elf32_Ehdr& header) +{ + + s->PutCString ("ELF Header\n"); + s->Printf ("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]); + s->Printf ("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1], header.e_ident[EI_MAG1]); + s->Printf ("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2], header.e_ident[EI_MAG2]); + s->Printf ("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3], header.e_ident[EI_MAG3]); + s->Printf ("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]); + s->Printf ("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]); + DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]); + s->Printf ("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]); + s->Printf ("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]); + + s->Printf("e_type = 0x%4.4x ", header.e_type); + DumpELFHeader_e_type(s, header.e_type); + s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine); + s->Printf("e_version = 0x%8.8x\n", header.e_version); + s->Printf("e_entry = 0x%8.8x\n", header.e_entry); + s->Printf("e_phoff = 0x%8.8x\n", header.e_phoff); + s->Printf("e_shoff = 0x%8.8x\n", header.e_shoff); + s->Printf("e_flags = 0x%8.8x\n", header.e_flags); + s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize); + s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize); + s->Printf("e_phnum = 0x%4.4x\n", header.e_phnum); + s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize); + s->Printf("e_shnum = 0x%4.4x\n", header.e_shnum); + s->Printf("e_shstrndx = 0x%4.4x\n", header.e_shstrndx); +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_type +// +// Dump an token value for the ELF header member e_type +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_type(Stream *s, uint16_t e_type) +{ + switch (e_type) + { + case ET_NONE: *s << "ET_NONE"; break; + case ET_REL: *s << "ET_REL"; break; + case ET_EXEC: *s << "ET_EXEC"; break; + case ET_DYN: *s << "ET_DYN"; break; + case ET_CORE: *s << "ET_CORE"; break; + default: + break; + } +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_ident_EI_DATA +// +// Dump an token value for the ELF header member e_ident[EI_DATA] +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s, uint16_t ei_data) +{ + switch (ei_data) + { + case ELFDATANONE: *s << "ELFDATANONE"; break; + case ELFDATA2LSB: *s << "ELFDATA2LSB - Little Endian"; break; + case ELFDATA2MSB: *s << "ELFDATA2MSB - Big Endian"; break; + default: + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader +// +// Dump a single ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader(Stream *s, const Elf32_Phdr& ph) +{ + DumpELFProgramHeader_p_type(s, ph.p_type); + s->Printf(" %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x (", ph.p_offset, ph.p_vaddr, ph.p_paddr, ph.p_filesz, ph.p_memsz, ph.p_flags); + DumpELFProgramHeader_p_flags(s, ph.p_flags); + s->Printf(") %8.8x", ph.p_align); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_type +// +// Dump an token value for the ELF program header member p_type which +// describes the type of the program header +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, Elf32_Word p_type) +{ + const int kStrWidth = 10; + switch (p_type) + { + CASE_AND_STREAM(s, PT_NULL , kStrWidth); + CASE_AND_STREAM(s, PT_LOAD , kStrWidth); + CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, PT_INTERP , kStrWidth); + CASE_AND_STREAM(s, PT_NOTE , kStrWidth); + CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, PT_PHDR , kStrWidth); + default: + s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_flags +// +// Dump an token value for the ELF program header member p_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, Elf32_Word p_flags) +{ + *s << ((p_flags & PF_X) ? "PF_X" : " ") + << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ') + << ((p_flags & PF_W) ? "PF_W" : " ") + << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ') + << ((p_flags & PF_R) ? "PF_R" : " "); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeaders +// +// Dump all of the ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeaders(Stream *s) +{ + if (ParseProgramHeaders()) + { + s->PutCString("Program Headers\n"); + s->PutCString("IDX p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align\n"); + s->PutCString("==== ---------- -------- -------- -------- -------- -------- ------------------------- --------\n"); + + uint32_t idx = 0; + ProgramHeaderCollConstIter pos; + + for (pos = m_program_headers.begin(); pos != m_program_headers.end(); ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFileELF::DumpELFProgramHeader(s, *pos); + s->EOL(); + } + } +} + + +//---------------------------------------------------------------------- +// DumpELFSectionHeader +// +// Dump a single ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader(Stream *s, const Elf32_Shdr& sh) +{ + s->Printf ("%8.8x ", sh.sh_name); + DumpELFSectionHeader_sh_type(s, sh.sh_type); + s->Printf (" %8.8x (", sh.sh_flags); + DumpELFSectionHeader_sh_flags(s, sh.sh_flags); + s->Printf (") %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x", + sh.sh_addr, sh.sh_offset, sh.sh_size, sh.sh_link, sh.sh_info, sh.sh_addralign, sh.sh_entsize); +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_type +// +// Dump an token value for the ELF section header member sh_type which +// describes the type of the section +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, Elf32_Word sh_type) +{ + const int kStrWidth = 12; + switch (sh_type) + { + CASE_AND_STREAM(s, SHT_NULL , kStrWidth); + CASE_AND_STREAM(s, SHT_PROGBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_SYMTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_STRTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_RELA , kStrWidth); + CASE_AND_STREAM(s, SHT_HASH , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, SHT_NOTE , kStrWidth); + CASE_AND_STREAM(s, SHT_NOBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_REL , kStrWidth); + CASE_AND_STREAM(s, SHT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNSYM , kStrWidth); + CASE_AND_STREAM(s, SHT_LOPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_HIPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_LOUSER , kStrWidth); + CASE_AND_STREAM(s, SHT_HIUSER , kStrWidth); + default: + s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_flags +// +// Dump an token value for the ELF section header member sh_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s, Elf32_Word sh_flags) +{ + *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ") + << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ') + << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ") + << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ') + << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " "); +} +//---------------------------------------------------------------------- +// DumpELFSectionHeaders +// +// Dump all of the ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeaders(Stream *s) +{ + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + s->PutCString("Section Headers\n"); + s->PutCString("IDX name type flags addr offset size link info addralgn entsize Name\n"); + s->PutCString("==== -------- ------------ -------------------------------- -------- -------- -------- -------- -------- -------- -------- ====================\n"); + + uint32_t idx = 0; + SectionHeaderCollConstIter pos; + + for (pos = m_section_headers.begin(); pos != m_section_headers.end(); ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFileELF::DumpELFSectionHeader(s, *pos); + const char* section_name = m_shstr_data.PeekCStr(pos->sh_name); + if (section_name) + *s << ' ' << section_name << "\n"; + } + } +} + +static uint32_t +ELFMachineToMachCPU(Elf32_Half machine) +{ + switch (machine) + { + case EM_SPARC: return CPU_TYPE_SPARC; + case EM_386: return CPU_TYPE_I386; + case EM_68K: return CPU_TYPE_MC680x0; + case EM_88K: return CPU_TYPE_MC88000; + case EM_860: return CPU_TYPE_I860; + case EM_MIPS: return 8; // commented out in mach/machine.h + case EM_PPC: return CPU_TYPE_POWERPC; + case EM_PPC64: return CPU_TYPE_POWERPC64; + case EM_ARM: return 12; // commented out in mach/machine.h + } + return 0; +} + +bool +ObjectFileELF::GetTargetTriple (ConstString &target_triple) +{ + static ConstString g_target_triple; + + if (g_target_triple) + { + target_triple = g_target_triple; + } + else + { + std::string triple; + switch (m_header.e_machine) + { + case EM_SPARC: triple.assign("sparc-"); break; + case EM_386: triple.assign("i386-"); break; + case EM_68K: triple.assign("68k-"); break; + case EM_88K: triple.assign("88k-"); break; + case EM_860: triple.assign("i860-"); break; + case EM_MIPS: triple.assign("mips-"); break; + case EM_PPC: triple.assign("powerpc-"); break; + case EM_PPC64: triple.assign("powerpc64-"); break; + case EM_ARM: triple.assign("arm-"); break; + } + // TODO: determine if there is a vendor in the ELF? Default to "apple" for now + triple += "apple-"; + // TODO: determine if there is an OS in the ELF? Default to "darwin" for now + triple += "darwin10"; + g_target_triple.SetCString(triple.c_str()); + target_triple = g_target_triple; + } + return !target_triple.IsEmpty(); +} + + +//bool +//ELF32RuntimeFileParser::GetArch(ArchSpec &arch) const +//{ +// arch.SetCPUType(ELFMachineToMachCPU(m_header.e_machine)); +// arch.SetCPUSubtype(ArchSpec::eAny); +// return true; +//} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectFileELF::GetPluginName() +{ + return "ObjectFileELF"; +} + +const char * +ObjectFileELF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileELF::GetPluginVersion() +{ + return 1; +} + +void +ObjectFileELF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectFileELF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectFileELF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h new file mode 100644 index 000000000000..5d5778c78fbf --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -0,0 +1,197 @@ +//===-- ObjectFileELF.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFileELF_h_ +#define liblldb_ObjectFileELF_h_ + +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "elf.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileELF : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectFile * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectFileELF (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectFileELF(); + + virtual bool + ParseHeader (); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual size_t + GetAddressByteSize () const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual lldb_private::SectionList * + GetSectionList(); + + virtual void + Dump (lldb_private::Stream *s); + + virtual bool + GetTargetTriple (lldb_private::ConstString &target_triple); + + virtual bool + GetUUID (lldb_private::UUID* uuid); + + virtual uint32_t + GetDependentModules(lldb_private::FileSpecList& files); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + typedef std::vector ProgramHeaderColl; + typedef ProgramHeaderColl::iterator ProgramHeaderCollIter; + typedef ProgramHeaderColl::const_iterator ProgramHeaderCollConstIter; + + typedef std::vector SectionHeaderColl; + typedef SectionHeaderColl::iterator SectionHeaderCollIter; + typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; + + Elf32_Ehdr m_header; + ProgramHeaderColl m_program_headers; + SectionHeaderColl m_section_headers; + mutable std::auto_ptr m_sections_ap; + mutable std::auto_ptr m_symtab_ap; + lldb_private::DataExtractor m_shstr_data; + + size_t + ParseSections (); + + size_t + ParseSymtab (bool minimize); + +private: + + // ELF header dump routines + static void + DumpELFHeader (lldb_private::Stream *s, + const Elf32_Ehdr& header); + + static void + DumpELFHeader_e_ident_EI_DATA (lldb_private::Stream *s, + uint16_t ei_data); + static void + DumpELFHeader_e_type (lldb_private::Stream *s, + uint16_t e_type); + + // ELF program header dump routines + void + DumpELFProgramHeaders (lldb_private::Stream *s); + + static void + DumpELFProgramHeader (lldb_private::Stream *s, + const Elf32_Phdr& ph); + + static void + DumpELFProgramHeader_p_type (lldb_private::Stream *s, + Elf32_Word p_type); + + static void + DumpELFProgramHeader_p_flags (lldb_private::Stream *s, + Elf32_Word p_flags); + + // ELF section header dump routines + void + DumpELFSectionHeaders (lldb_private::Stream *s); + + static void + DumpELFSectionHeader (lldb_private::Stream *s, + const Elf32_Shdr& sh); + + static void + DumpELFSectionHeader_sh_type (lldb_private::Stream *s, + Elf32_Word sh_type); + + static void + DumpELFSectionHeader_sh_flags (lldb_private::Stream *s, + Elf32_Word sh_flags); + + size_t + ParseProgramHeaders (); + + size_t + ParseSectionHeaders (); + + size_t + GetSectionHeaderStringTable (); + + uint32_t + GetSectionIndexByName (const char *name); +}; + +#endif // #ifndef liblldb_ObjectFileELF_h_ diff --git a/lldb/source/Plugins/ObjectFile/ELF/elf.h b/lldb/source/Plugins/ObjectFile/ELF/elf.h new file mode 100644 index 000000000000..9d08119c5979 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/elf.h @@ -0,0 +1,240 @@ +//===-- elf.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __elf_h__ +#define __elf_h__ + +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; + + +#define EI_NIDENT 16 + +//---------------------------------------------------------------------- +// ELF Header +//---------------------------------------------------------------------- +typedef struct Elf32_Ehdr_Tag +{ + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +//---------------------------------------------------------------------- +// e_type +// +// This member identifies the object file type. +//---------------------------------------------------------------------- +#define ET_NONE 0 // No file type +#define ET_REL 1 // Relocatable file +#define ET_EXEC 2 // Executable file +#define ET_DYN 3 // Shared object file +#define ET_CORE 4 // Core file +#define ET_LOPROC 0xff00 // Processor-specific +#define ET_HIPROC 0xffff // Processor-specific + +//---------------------------------------------------------------------- +// e_machine +// +// Machine Type +//---------------------------------------------------------------------- +#define EM_NONE 0 // No machine +#define EM_M32 1 // AT&T WE 32100 +#define EM_SPARC 2 // SPARC +#define EM_386 3 // Intel 80386 +#define EM_68K 4 // Motorola 68000 +#define EM_88K 5 // Motorola 88000 +#define EM_860 7 // Intel 80860 +#define EM_MIPS 8 // MIPS RS3000 +#define EM_PPC 20 // PowerPC +#define EM_PPC64 21 // PowerPC64 +#define EM_ARM 40 // ARM + + +//---------------------------------------------------------------------- +// e_ident indexes +//---------------------------------------------------------------------- +#define EI_MAG0 0 // File identification +#define EI_MAG1 1 // File identification +#define EI_MAG2 2 // File identification +#define EI_MAG3 3 // File identification +#define EI_CLASS 4 // File class +#define EI_DATA 5 // Data encoding +#define EI_VERSION 6 // File version +#define EI_PAD 7 // Start of padding bytes + + +//---------------------------------------------------------------------- +// EI_DATA definitions +//---------------------------------------------------------------------- +#define ELFDATANONE 0 // Invalid data encoding +#define ELFDATA2LSB 1 // Little Endian +#define ELFDATA2MSB 2 // Big Endian + +//---------------------------------------------------------------------- +// Section Header +//---------------------------------------------------------------------- +typedef struct Elf32_Shdr_Tag +{ + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +//---------------------------------------------------------------------- +// Section Types (sh_type) +//---------------------------------------------------------------------- +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +//---------------------------------------------------------------------- +// Special Section Indexes +//---------------------------------------------------------------------- +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +//---------------------------------------------------------------------- +// Section Attribute Flags (sh_flags) +//---------------------------------------------------------------------- +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + + +//---------------------------------------------------------------------- +// Symbol Table Entry Header +//---------------------------------------------------------------------- +typedef struct Elf32_Sym_Tag +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + + +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +// ST_BIND +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +// ST_TYPE +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +//---------------------------------------------------------------------- +// Relocation Entries +//---------------------------------------------------------------------- +typedef struct Elf32_Rel_Tag +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct Elf32_Rela_Tag +{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsignedchar)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsignedchar)(t)) + + +//---------------------------------------------------------------------- +// Program Headers +//---------------------------------------------------------------------- +typedef struct Elf32_Phdr_Tag +{ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +//---------------------------------------------------------------------- +// Program Header Type (p_type) +//---------------------------------------------------------------------- +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +#define PF_X (1 << 0) // executable +#define PF_W (1 << 1) // writable +#define PF_R (1 << 2) // readable + + +#endif // __elf_h__ diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp new file mode 100644 index 000000000000..682a116d2138 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -0,0 +1,1311 @@ +//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileMachO.h" + +#include +#include + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" + +#ifndef S_DTRACE_DOF +// section contains DTrace Object Format +#define S_DTRACE_DOF 0xf +#endif + +#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS +// section with only lazy symbol pointers to lazy loaded dylibs +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#endif + +using namespace lldb; +using namespace lldb_private; + + +void +ObjectFileMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectFileMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectFileMachO::GetPluginNameStatic() +{ + return "object-file.mach-o"; +} + +const char * +ObjectFileMachO::GetPluginDescriptionStatic() +{ + return "Mach-o object file reader (32 and 64 bit)"; +} + + +ObjectFile * +ObjectFileMachO::CreateInstance (Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) +{ + if (ObjectFileMachO::MagicBytesMatch(dataSP)) + { + std::auto_ptr objfile_ap(new ObjectFileMachO (module, dataSP, file, offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + + +static uint32_t +MachHeaderSizeFromMagic(uint32_t magic) +{ + switch (magic) + { + case MH_MAGIC: + case MH_CIGAM: + return sizeof(struct mach_header); + + case MH_MAGIC_64: + case MH_CIGAM_64: + return sizeof(struct mach_header_64); + break; + + default: + break; + } + return 0; +} + + +bool +ObjectFileMachO::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return MachHeaderSizeFromMagic(magic) != 0; +} + + +ObjectFileMachO::ObjectFileMachO(Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) : + ObjectFile(module, file, offset, length, dataSP), + m_mutex (Mutex::eMutexTypeRecursive), + m_header(), + m_sections_ap(), + m_symtab_ap() +{ + ::bzero (&m_header, sizeof(m_header)); + ::bzero (&m_dysymtab, sizeof(m_dysymtab)); +} + + +ObjectFileMachO::~ObjectFileMachO() +{ +} + + +bool +ObjectFileMachO::ParseHeader () +{ + lldb_private::Mutex::Locker locker(m_mutex); + bool can_parse = false; + uint32_t offset = 0; + m_data.SetByteOrder (eByteOrderHost); + // Leave magic in the original byte order + m_header.magic = m_data.GetU32(&offset); + switch (m_header.magic) + { + case MH_MAGIC: + m_data.SetByteOrder (eByteOrderHost); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + m_data.SetByteOrder (eByteOrderHost); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + case MH_CIGAM: + m_data.SetByteOrder(eByteOrderHost == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + m_data.SetByteOrder(eByteOrderHost == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + default: + break; + } + + if (can_parse) + { + m_data.GetU32(&offset, &m_header.cputype, 6); + + ArchSpec mach_arch(m_header.cputype, m_header.cpusubtype); + if (mach_arch == m_module->GetArchitecture()) + { + // Read in all only the load command data + DataBufferSP data_sp(m_file.ReadFileContents(m_offset, m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic))); + m_data.SetData (data_sp); + return true; + } + } + else + { + memset(&m_header, 0, sizeof(struct mach_header)); + } + return false; +} + + +ByteOrder +ObjectFileMachO::GetByteOrder () const +{ + lldb_private::Mutex::Locker locker(m_mutex); + return m_data.GetByteOrder (); +} + + +size_t +ObjectFileMachO::GetAddressByteSize () const +{ + lldb_private::Mutex::Locker locker(m_mutex); + return m_data.GetAddressByteSize (); +} + + +Symtab * +ObjectFileMachO::GetSymtab() +{ + lldb_private::Mutex::Locker locker(m_mutex); + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + ParseSymtab(false); + } + return m_symtab_ap.get(); +} + + +SectionList * +ObjectFileMachO::GetSectionList() +{ + lldb_private::Mutex::Locker locker(m_mutex); + if (m_sections_ap.get() == NULL) + { + m_sections_ap.reset(new SectionList()); + ParseSections(); + } + return m_sections_ap.get(); +} + + +size_t +ObjectFileMachO::ParseSections () +{ + lldb::user_id_t segID = 0; + lldb::user_id_t sectID = 0; + struct segment_command_64 load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + //bool dump_sections = false; + for (i=0; i(strlen(load_cmd.segname), sizeof(load_cmd.segname))); + // Use a segment ID of the segment index shifted left by 8 so they + // never conflict with any of the sections. + SectionSP segment_sp; + if (segment_name) + { + segment_sp.reset(new Section (NULL, + GetModule(), // Module to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + segment_name, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + load_cmd.vmaddr, // File VM address == addresses as they are found in the object file + load_cmd.vmsize, // VM size in bytes of this section + load_cmd.fileoff, // Offset to the data for this section in the file + load_cmd.filesize, // Size in bytes of this section as found in the the file + load_cmd.flags)); // Flags for this section + + m_sections_ap->AddSection(segment_sp); + } + + struct section_64 sect64; + ::bzero (§64, sizeof(sect64)); + // Push a section into our mach sections for the section at + // index zero (NO_SECT) + m_mach_sections.push_back(sect64); + uint32_t segment_sect_idx; + const lldb::user_id_t first_segment_sectID = sectID + 1; + + + const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8; + for (segment_sect_idx=0; segment_sect_idx(strlen(sect64.sectname), sizeof(sect64.sectname))); + if (!segment_name) + { + // We have a segment with no name so we need to conjure up + // segments that correspond to the section's segname if there + // isn't already such a section. If there is such a section, + // we resize the section so that it spans all sections. + // We also mark these sections as fake so address matches don't + // hit if they land in the gaps between the child sections. + segment_name.SetTrimmedCStringWithLength(sect64.segname, sizeof(sect64.segname)); + segment_sp = m_sections_ap->FindSectionByName (segment_name); + if (segment_sp.get()) + { + Section *segment = segment_sp.get(); + // Grow the section size as needed. + const lldb::addr_t sect64_min_addr = sect64.addr; + const lldb::addr_t sect64_max_addr = sect64_min_addr + sect64.size; + const lldb::addr_t curr_seg_byte_size = segment->GetByteSize(); + const lldb::addr_t curr_seg_min_addr = segment->GetFileAddress(); + const lldb::addr_t curr_seg_max_addr = curr_seg_min_addr + curr_seg_byte_size; + if (sect64_min_addr >= curr_seg_min_addr) + { + const lldb::addr_t new_seg_byte_size = sect64_max_addr - curr_seg_min_addr; + // Only grow the section size if needed + if (new_seg_byte_size > curr_seg_byte_size) + segment->SetByteSize (new_seg_byte_size); + } + else + { + // We need to change the base address of the segment and + // adjust the child section offsets for all existing children. + const lldb::addr_t slide_amount = sect64_min_addr - curr_seg_min_addr; + segment->Slide(slide_amount, false); + segment->GetChildren().Slide (-slide_amount, false); + segment->SetByteSize (curr_seg_max_addr - sect64_min_addr); + } + } + else + { + // Create a fake section for the section's named segment + segment_sp.reset(new Section(segment_sp.get(), // Parent section + GetModule(), // Module to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + segment_name, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + sect64.addr, // File VM address == addresses as they are found in the object file + sect64.size, // VM size in bytes of this section + sect64.offset, // Offset to the data for this section in the file + sect64.offset ? sect64.size : 0, // Size in bytes of this section as found in the the file + load_cmd.flags)); // Flags for this section + segment_sp->SetIsFake(true); + m_sections_ap->AddSection(segment_sp); + } + } + assert (segment_sp.get()); + + uint32_t mach_sect_type = sect64.flags & SECTION_TYPE; + static ConstString g_sect_name_objc_data ("__objc_data"); + static ConstString g_sect_name_objc_msgrefs ("__objc_msgrefs"); + static ConstString g_sect_name_objc_selrefs ("__objc_selrefs"); + static ConstString g_sect_name_objc_classrefs ("__objc_classrefs"); + static ConstString g_sect_name_objc_superrefs ("__objc_superrefs"); + static ConstString g_sect_name_objc_const ("__objc_const"); + static ConstString g_sect_name_objc_classlist ("__objc_classlist"); + static ConstString g_sect_name_cfstring ("__cfstring"); + SectionType sect_type = eSectionTypeOther; + + if (section_name == g_sect_name_objc_selrefs) + { + sect_type = eSectionTypeDataCStringPointers; + } + else if (section_name == g_sect_name_objc_msgrefs) + { + sect_type = eSectionTypeDataObjCMessageRefs; + } + else if (section_name == g_sect_name_objc_data || + section_name == g_sect_name_objc_classrefs || + section_name == g_sect_name_objc_superrefs || + section_name == g_sect_name_objc_const || + section_name == g_sect_name_objc_classlist) + { + sect_type = eSectionTypeDataPointers; + } + else if (section_name == g_sect_name_cfstring) + { + sect_type = eSectionTypeDataObjCCFStrings; + } + + if (sect_type == eSectionTypeOther) + { + switch (mach_sect_type) + { + // TODO: categorize sections by other flags for regular sections + case S_REGULAR: + + sect_type = eSectionTypeOther; + break; + case S_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_CSTRING_LITERALS: sect_type = eSectionTypeDataCString; break; // section with only literal C strings + case S_4BYTE_LITERALS: sect_type = eSectionTypeData4; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: sect_type = eSectionTypeData8; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: sect_type = eSectionTypeCode; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for termination + case S_COALESCED: sect_type = eSectionTypeOther; break; + case S_GB_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_INTERPOSING: sect_type = eSectionTypeCode; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: sect_type = eSectionTypeData16; break; // section with only 16 byte literals + case S_DTRACE_DOF: sect_type = eSectionTypeDebug; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; + default: break; + } + } + + SectionSP section_sp(new Section(segment_sp.get(), + GetModule(), + ++sectID, + section_name, + sect_type, + sect64.addr - segment_sp->GetFileAddress(), + sect64.size, + sect64.offset, + sect64.offset == 0 ? 0 : sect64.size, + sect64.flags)); + segment_sp->GetChildren().AddSection(section_sp); + + if (segment_sp->IsFake()) + { + segment_sp.reset(); + segment_name.Clear(); + } + } + if (m_header.filetype == MH_DSYM) + { + if (first_segment_sectID <= sectID) + { + lldb::user_id_t sect_uid; + for (sect_uid = first_segment_sectID; sect_uid <= sectID; ++sect_uid) + { + SectionSP curr_section_sp(segment_sp->GetChildren().FindSectionByID (sect_uid)); + SectionSP next_section_sp; + if (sect_uid + 1 <= sectID) + next_section_sp = segment_sp->GetChildren().FindSectionByID (sect_uid+1); + + if (curr_section_sp.get()) + { + if (curr_section_sp->GetByteSize() == 0) + { + if (next_section_sp.get() != NULL) + curr_section_sp->SetByteSize ( next_section_sp->GetFileAddress() - curr_section_sp->GetFileAddress() ); + else + curr_section_sp->SetByteSize ( load_cmd.vmsize ); + } + } + } + } + } + } + } + } + else if (load_cmd.cmd == LC_DYSYMTAB) + { + m_dysymtab.cmd = load_cmd.cmd; + m_dysymtab.cmdsize = load_cmd.cmdsize; + m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2); + } + + offset = load_cmd_offset + load_cmd.cmdsize; + } +// if (dump_sections) +// { +// StreamFile s(stdout); +// m_sections_ap->Dump(&s, true); +// } + return sectID; // Return the number of sections we registered with the module +} + +class MachSymtabSectionInfo +{ +public: + + MachSymtabSectionInfo (SectionList *section_list) : + m_section_list (section_list), + m_section_infos() + { + // Get the number of sections down to a depth of 1 to include + // all segments and their sections, but no other sections that + // may be added for debug map or + m_section_infos.resize(section_list->GetNumSections(1)); + } + + + Section * + GetSection (uint8_t n_sect, addr_t file_addr) + { + if (n_sect == 0) + return NULL; + if (n_sect < m_section_infos.size()) + { + if (m_section_infos[n_sect].section == NULL) + { + Section *section = m_section_list->FindSectionByID (n_sect).get(); + m_section_infos[n_sect].section = section; + assert (section != NULL); + m_section_infos[n_sect].vm_range.SetBaseAddress (section->GetFileAddress()); + m_section_infos[n_sect].vm_range.SetByteSize (section->GetByteSize()); + } + if (m_section_infos[n_sect].vm_range.Contains(file_addr)) + return m_section_infos[n_sect].section; + } + return m_section_list->FindSectionContainingFileAddress(file_addr).get(); + } + +protected: + struct SectionInfo + { + SectionInfo () : + vm_range(), + section (NULL) + { + } + + VMRange vm_range; + Section *section; + }; + SectionList *m_section_list; + std::vector m_section_infos; +}; + + + +size_t +ObjectFileMachO::ParseSymtab (bool minimize) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "ObjectFileMachO::ParseSymtab () module = %s", + m_file.GetFilename().AsCString("")); + struct symtab_command symtab_load_command; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + for (i=0; iGetBytes(); +// DataExtractor symtab_data(symtab_data_sp, endian, addr_size); +// DataExtractor strtab_data(strtab_data_sp, endian, addr_size); + + static ConstString g_segment_name_TEXT ("__TEXT"); + static ConstString g_segment_name_DATA ("__DATA"); + static ConstString g_segment_name_OBJC ("__OBJC"); + static ConstString g_section_name_eh_frame ("__eh_frame"); + SectionSP text_section_sp(section_list->FindSectionByName(g_segment_name_TEXT)); + SectionSP data_section_sp(section_list->FindSectionByName(g_segment_name_DATA)); + SectionSP objc_section_sp(section_list->FindSectionByName(g_segment_name_OBJC)); + SectionSP eh_frame_section_sp; + if (text_section_sp.get()) + eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName (g_section_name_eh_frame); + else + eh_frame_section_sp = section_list->FindSectionByName (g_section_name_eh_frame); + + uint8_t TEXT_eh_frame_sectID = eh_frame_section_sp.get() ? eh_frame_section_sp->GetID() : NO_SECT; + //uint32_t symtab_offset = 0; + const uint8_t* nlist_data = symtab_data_sp->GetBytes(); + assert (symtab_data_sp->GetByteSize()/nlist_size >= symtab_load_command.nsyms); + + + if (endian != eByteOrderHost) + { + // ... + assert (!"UNIMPLEMENTED: Swap all nlist entries"); + } + uint32_t N_SO_index = UINT_MAX; + + MachSymtabSectionInfo section_info (section_list); + std::vector N_FUN_indexes; + std::vector N_NSYM_indexes; + std::vector N_INCL_indexes; + std::vector N_BRAC_indexes; + std::vector N_COMM_indexes; + uint32_t nlist_idx = 0; + Symbol *symbol_ptr = NULL; + + uint32_t sym_idx = 0; + Symbol *sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + uint32_t num_syms = symtab->GetNumSymbols(); + + //symtab->Reserve (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + for (nlist_idx = 0; nlist_idx < symtab_load_command.nsyms; ++nlist_idx) + { + struct nlist_64 nlist; + if (bit_width_32) + { + struct nlist* nlist32_ptr = (struct nlist*)(nlist_data + (nlist_idx * nlist_size)); + nlist.n_un.n_strx = nlist32_ptr->n_un.n_strx; + nlist.n_type = nlist32_ptr->n_type; + nlist.n_sect = nlist32_ptr->n_sect; + nlist.n_desc = nlist32_ptr->n_desc; + nlist.n_value = nlist32_ptr->n_value; + } + else + { + nlist = *((struct nlist_64*)(nlist_data + (nlist_idx * nlist_size))); + } + + SymbolType type = eSymbolTypeInvalid; + const char* symbol_name = &strtab_data[nlist.n_un.n_strx]; + if (symbol_name[0] == '\0') + symbol_name = NULL; + Section* symbol_section = NULL; + bool add_nlist = true; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + + assert (sym_idx < num_syms); + + sym[sym_idx].SetDebug (is_debug); + + if (is_debug) + { + switch (nlist.n_type) + { + case N_GSYM: // global symbol: name,,NO_SECT,type,0 + // Sometimes the N_GSYM value contains the address. + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeGlobal; + break; + + case N_FNAME: // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeFunction; + break; + + case N_FUN: // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeFunction; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_FUN_indexes.push_back(sym_idx); + } + else + { + type = eSymbolTypeFunctionEnd; + + if ( !N_FUN_indexes.empty() ) + { + // Copy the size of the function into the original STAB entry so we don't have + // to hunt for it later + symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value); + N_FUN_indexes.pop_back(); + // We dont' really need the end function STAB as it contains the size which + // we already placed with the original symbol, so don't add it if we want a + // minimal symbol table + if (minimize) + add_nlist = false; + } + } + break; + + case N_STSYM: // static symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeStatic; + break; + + case N_LCSYM: // .lcomm symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeCommonBlock; + break; + + case N_BNSYM: + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + if (minimize) + { + // Skip these if we want minimal symbol tables + add_nlist = false; + } + else + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_NSYM_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + } + break; + + case N_ENSYM: + // Set the size of the N_BNSYM to the terminating index of this N_ENSYM + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if (minimize) + { + // Skip these if we want minimal symbol tables + add_nlist = false; + } + else + { + if ( !N_NSYM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_NSYM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_NSYM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + } + break; + + + case N_OPT: // emitted with gcc2_compiled and in gcc source + type = eSymbolTypeCompiler; + break; + + case N_RSYM: // register sym: name,,NO_SECT,type,register + type = eSymbolTypeVariable; + break; + + case N_SLINE: // src line: 0,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + case N_SSYM: // structure elt: name,,NO_SECT,type,struct_offset + type = eSymbolTypeVariableType; + break; + + case N_SO: + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + if (N_SO_index == UINT_MAX) + { + // Skip the extra blank N_SO entries that happen when the entire + // path is contained in the second consecutive N_SO STAB. + if (minimize) + add_nlist = false; + } + else + { + // Set the size of the N_SO to the terminating index of this N_SO + // so that we can always skip the entire N_SO if we need to navigate + // more quickly at the source level when parsing STABS + symbol_ptr = symtab->SymbolAtIndex(N_SO_index); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + } + N_NSYM_indexes.clear(); + N_INCL_indexes.clear(); + N_BRAC_indexes.clear(); + N_COMM_indexes.clear(); + N_FUN_indexes.clear(); + N_SO_index = UINT_MAX; + } + else if (symbol_name[0] == '/') + { + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_SO_index = sym_idx; + } + break; + + case N_OSO: // object file name: name,,0,0,st_mtime + type = eSymbolTypeObjectFile; + break; + + case N_LSYM: // local sym: name,,NO_SECT,type,offset + type = eSymbolTypeLocal; + break; + + //---------------------------------------------------------------------- + // INCL scopes + //---------------------------------------------------------------------- + case N_BINCL: // include file beginning: name,,NO_SECT,0,sum + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_INCL_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + case N_EINCL: // include file end: name,,NO_SECT,0,0 + + // Set the size of the N_BINCL to the terminating index of this N_EINCL + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_INCL_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_INCL_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_SOL: // #included file name: name,,n_sect,0,address + type = eSymbolTypeHeaderFile; + break; + + case N_PARAMS: // compiler parameters: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_VERSION: // compiler version: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_OLEVEL: // compiler -O level: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_PSYM: // parameter: name,,NO_SECT,type,offset + type = eSymbolTypeVariable; + break; + + case N_ENTRY: // alternate entry: name,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + //---------------------------------------------------------------------- + // Left and Right Braces + //---------------------------------------------------------------------- + case N_LBRAC: // left bracket: 0,,NO_SECT,nesting level,address + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_BRAC_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_RBRAC: // right bracket: 0,,NO_SECT,nesting level,address + // Set the size of the N_LBRAC to the terminating index of this N_RBRAC + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if ( !N_BRAC_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_BRAC_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_EXCL: // deleted include file: name,,NO_SECT,0,sum + type = eSymbolTypeHeaderFile; + break; + + //---------------------------------------------------------------------- + // COMM scopes + //---------------------------------------------------------------------- + case N_BCOMM: // begin common: name,,NO_SECT,0,0 + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + type = eSymbolTypeScopeBegin; + N_COMM_indexes.push_back(sym_idx); + break; + + case N_ECOML: // end common (local name): 0,,n_sect,0,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // Fall through + + case N_ECOMM: // end common: name,,n_sect,0,0 + // Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_COMM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_COMM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_LENG: // second stab entry with length information + type = eSymbolTypeAdditional; + break; + + default: break; + } + } + else + { + //uint8_t n_pext = N_PEXT & nlist.n_type; + uint8_t n_type = N_TYPE & nlist.n_type; + sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0); + + if (symbol_name && ::strstr (symbol_name, ".objc") == symbol_name) + { + type = eSymbolTypeRuntime; + } + else + { + switch (n_type) + { + case N_INDR: // Fall through + case N_PBUD: // Fall through + case N_UNDF: + type = eSymbolTypeExtern; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + assert(symbol_section != NULL); + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->GetAllFlagBits() & SECTION_TYPE; + + switch (section_type) + { + case S_REGULAR: break; // regular section + //case S_ZEROFILL: type = eSymbolTypeData; break; // zero fill on demand section + case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings + case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination + //case S_COALESCED: type = eSymbolType; break; // section contains symbols that are to be coalesced + //case S_GB_ZEROFILL: type = eSymbolTypeData; break; // zero fill on demand section (that can be larger than 4 gigabytes) + case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals + case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; + default: break; + } + + if (type == eSymbolTypeInvalid) + { + const char *symbol_sect_name = symbol_section->GetName().AsCString(); + if (symbol_section->IsDescendant (text_section_sp.get())) + { + if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE | S_ATTR_SOME_INSTRUCTIONS)) + type = eSymbolTypeData; + else + type = eSymbolTypeCode; + } + else + if (symbol_section->IsDescendant(data_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name) + { + type = eSymbolTypeException; + } + else + { + type = eSymbolTypeData; + } + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name) + { + type = eSymbolTypeTrampoline; + } + else + if (symbol_section->IsDescendant(objc_section_sp.get())) + { + type = eSymbolTypeRuntime; + } + } + } + break; + } + } + } + + if (add_nlist) + { + bool symbol_name_is_mangled = false; + if (symbol_name && symbol_name[0] == '_') + { + symbol_name_is_mangled = symbol_name[1] == '_'; + symbol_name++; // Skip the leading underscore + } + uint64_t symbol_value = nlist.n_value; + if (symbol_section != NULL) + symbol_value -= symbol_section->GetFileAddress(); + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (symbol_name) + sym[sym_idx].GetMangled().SetValue(symbol_name, symbol_name_is_mangled); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress().SetSection (symbol_section); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress().SetOffset (symbol_value); + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + + } + + + // STAB N_GSYM entries end up having a symbol type eSymbolTypeGlobal and when the symbol value + // is zero, the address of the global ends up being in a non-STAB entry. Try and fix up all + // such entries by figuring out what the address for the global is by looking up this non-STAB + // entry and copying the value into the debug symbol's value to save us the hassle in the + // debug symbol parser. + + Symbol *global_symbol = NULL; + for (nlist_idx = 0; + nlist_idx < symtab_load_command.nsyms && (global_symbol = symtab->FindSymbolWithType(eSymbolTypeGlobal, nlist_idx)) != NULL; + nlist_idx++) + { + if (global_symbol->GetValue().GetFileAddress() == 0) + { + std::vector indexes; + if (symtab->AppendSymbolIndexesWithName(global_symbol->GetMangled().GetName(), indexes) > 0) + { + std::vector::const_iterator pos; + std::vector::const_iterator end = indexes.end(); + for (pos = indexes.begin(); pos != end; ++pos) + { + symbol_ptr = symtab->SymbolAtIndex(*pos); + if (symbol_ptr != global_symbol && symbol_ptr->IsDebug() == false) + { + global_symbol->SetValue(symbol_ptr->GetValue()); + break; + } + } + } + } + } + // Now synthesize indirect symbols + if (m_dysymtab.nindirectsyms != 0) + { + DataBufferSP indirect_symbol_indexes_sp(m_file.ReadFileContents(m_offset + m_dysymtab.indirectsymoff, m_dysymtab.nindirectsyms * 4)); + + if (indirect_symbol_indexes_sp && indirect_symbol_indexes_sp->GetByteSize()) + { + DataExtractor indirect_symbol_index_data (indirect_symbol_indexes_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + for (uint32_t sect_idx = 1; sect_idx < m_mach_sections.size(); ++sect_idx) + { + if ((m_mach_sections[sect_idx].flags & SECTION_TYPE) == S_SYMBOL_STUBS) + { + uint32_t symbol_stub_byte_size = m_mach_sections[sect_idx].reserved2; + if (symbol_stub_byte_size == 0) + continue; + + const uint32_t num_symbol_stubs = m_mach_sections[sect_idx].size / symbol_stub_byte_size; + + if (num_symbol_stubs == 0) + continue; + + const uint32_t symbol_stub_index_offset = m_mach_sections[sect_idx].reserved1; + uint32_t stub_sym_id = symtab_load_command.nsyms; + for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs; ++stub_idx) + { + const uint32_t symbol_stub_index = symbol_stub_index_offset + stub_idx; + const lldb::addr_t symbol_stub_addr = m_mach_sections[sect_idx].addr + (stub_idx * symbol_stub_byte_size); + uint32_t symbol_stub_offset = symbol_stub_index * 4; + if (indirect_symbol_index_data.ValidOffsetForDataOfSize(symbol_stub_offset, 4)) + { + const uint32_t symbol_index = indirect_symbol_index_data.GetU32 (&symbol_stub_offset); + + Symbol *stub_symbol = symtab->SymbolAtIndex(symbol_index); + if (stub_symbol) + { + Address so_addr(symbol_stub_addr, section_list); + + if (stub_symbol->GetType() == eSymbolTypeExtern) + { + // Change the external symbol into a trampoline that makes sense + // These symbols were N_UNDF N_EXT, and are useless to us, so we + // can re-use them so we don't have to make up a synthetic symbol + // for no good reason. + stub_symbol->SetType (eSymbolTypeTrampoline); + stub_symbol->SetExternal (false); + stub_symbol->GetAddressRangeRef().GetBaseAddress() = so_addr; + stub_symbol->GetAddressRangeRef().SetByteSize (symbol_stub_byte_size); + } + else + { + // Make a synthetic symbol to describe the trampoline stub + if (sym_idx >= num_syms) + { + sym = symtab->Resize (num_syms + 16); + num_syms = symtab->GetNumSymbols(); + } + sym[sym_idx].SetID (stub_sym_id++); + sym[sym_idx].GetMangled() = stub_symbol->GetMangled(); + sym[sym_idx].SetType (eSymbolTypeTrampoline); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress() = so_addr; + sym[sym_idx].GetAddressRangeRef().SetByteSize (symbol_stub_byte_size); + ++sym_idx; + } + } + } + } + } + } + } + } + + if (sym_idx != symtab->GetNumSymbols()) + symtab->Resize (sym_idx); + + return symtab->GetNumSymbols(); + } + } + offset = cmd_offset + symtab_load_command.cmdsize; + } + return 0; +} + + +void +ObjectFileMachO::Dump (Stream *s) +{ + lldb_private::Mutex::Locker locker(m_mutex); + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + if (m_header.magic == MH_MAGIC_64 || m_header.magic == MH_CIGAM_64) + s->PutCString("ObjectFileMachO64"); + else + s->PutCString("ObjectFileMachO32"); + + ArchSpec header_arch(m_header.cputype, m_header.cpusubtype); + + *s << ", file = '" << m_file << "', arch = " << header_arch.AsCString() << "\n"; + + if (m_sections_ap.get()) + m_sections_ap->Dump(s, NULL, true); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL); +} + + +bool +ObjectFileMachO::GetUUID (UUID* uuid) +{ + lldb_private::Mutex::Locker locker(m_mutex); + struct uuid_command load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + for (i=0; iSetBytes (uuid_bytes); + return true; + } + return false; + } + offset = cmd_offset + load_cmd.cmdsize; + } + return false; +} + + +uint32_t +ObjectFileMachO::GetDependentModules (FileSpecList& files) +{ + lldb_private::Mutex::Locker locker(m_mutex); + struct load_command load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t count = 0; + uint32_t i; + for (i=0; iGetArchitecture().AsCString()); + triple += "-apple-darwin"; + target_triple.SetCString(triple.c_str()); + if (target_triple) + return true; + return false; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectFileMachO::GetPluginName() +{ + return "ObjectFileMachO"; +} + +const char * +ObjectFileMachO::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileMachO::GetPluginVersion() +{ + return 1; +} + +void +ObjectFileMachO::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectFileMachO::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectFileMachO::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + + diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h new file mode 100644 index 000000000000..3ffeb242b7ca --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -0,0 +1,131 @@ +//===-- ObjectFileMachO.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFileMachO_h_ +#define liblldb_ObjectFileMachO_h_ + +#include +#include "lldb/Core/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileMachO : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static ObjectFile * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectFileMachO (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectFileMachO(); + + virtual bool + ParseHeader (); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual size_t + GetAddressByteSize () const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual lldb_private::SectionList * + GetSectionList(); + + virtual void + Dump (lldb_private::Stream *s); + + virtual bool + GetTargetTriple (lldb_private::ConstString &target_triple); + + virtual bool + GetUUID (lldb_private::UUID* uuid); + + virtual uint32_t + GetDependentModules (lldb_private::FileSpecList& files); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + mutable lldb_private::Mutex m_mutex; + struct mach_header m_header; + mutable std::auto_ptr m_sections_ap; + mutable std::auto_ptr m_symtab_ap; + + struct dysymtab_command m_dysymtab; + std::vector m_mach_segments; + std::vector m_mach_sections; + + size_t + ParseSections (); + + size_t + ParseSymtab (bool minimize); + +}; + +#endif // liblldb_ObjectFileMachO_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig b/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig new file mode 100644 index 000000000000..0bb089a653e8 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig @@ -0,0 +1,47 @@ +#!/usr/bin/perl + +use File::Basename; + +sub execute_command +{ + print join(' ', @_), "\n"; + if (scalar(@_) > 0) { + system(@_); + } else { + system($_[0]); + } +} + +my $infile = $ENV{SCRIPT_INPUT_FILE_1}; +my($in_basename, $in_dirname, $in_extension) = fileparse($infile, qr/\.[^.]*/); +my $outdir = "$ENV{DERIVED_FILE_DIR}"; +my $perl_wrap_c = "$outdir/${in_basename}_perl_wrap.c"; +mkdir "$ENV{OBJECT_FILE_DIR}"; +my $perl_wrap_o = "$ENV{OBJECT_FILE_DIR}/${in_basename}_perl_wrap.o"; +my $perl_module = "$outdir/${in_basename}.pm"; +my $header_paths = "-I'../../../../../debugcore/source' -I'../../../../../DebugBase'"; +my $framework_opts = "-F'$ENV{CONFIGURATION_BUILD_DIR}' "; +execute_command("/usr/bin/swig -shadow -perl5 -DHAS_BOOL $header_paths -outdir '$outdir' -o '$perl_wrap_c' '$infile'"); + +# Get any needed perl options for the next compile +my $ccopts = `perl -MExtUtils::Embed -e ccopts`; +my $libperl_dir = undef; +if ($ccopts =~ /-I(\/System.*CORE)/) +{ + $libperl_dir = $1; + print "libperl directory: '$libperl_dir'\n"; +} + +execute_command("cd '$ENV{OBJECT_FILE_DIR}' && ln -s '$libperl_dir/libperl.dylib'"); + + +# Strip out the default architectures it gave us, we will add them back with +# the $arch_opts below +$ccopts =~ s/-arch [a-z_0-9]+//g; + +# Get a list of our build architectures +my $arch_opts = "-arch " . join(' -arch ', split('\s+', $ENV{ARCHS})); + +execute_command("gcc -c -Dbool=char $arch_opts $ccopts $header_paths $framework_opts -I'$ENV{PROJECT_DIR}/source' '$perl_wrap_c' -o '$perl_wrap_o'"); + +execute_command("cp '$perl_module' '$ENV{CONFIGURATION_BUILD_DIR}/$ENV{SHARED_SUPPORT_FOLDER_PATH}'"); \ No newline at end of file diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl b/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl new file mode 100644 index 000000000000..a6cf6ce2396c --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +use strict; +my $config_file = "$ENV{SCRIPT_OUTPUT_FILE_0}"; + +# Define the tests we need to run during this configuration +my @config_tests = ( + { + NAME => "HAVE_64_BIT_MACH_EXCEPTIONS", + TEST => "-e '$ENV{SDKROOT}/usr/include/mach/mach_exc.defs'", + COMMENT => "// Defined if we can use 64 bit mach exceptions", + FAIL => "#undef HAVE_64_BIT_MACH_EXCEPTIONS\ +#define mach_exception_data_t exception_data_t\ +#define mach_exception_data_type_t exception_data_type_t\ +#define mach_exc_server exc_server\ +#define MACH_EXCEPTION_CODES 0\n", + SUCCESS => "#define HAVE_64_BIT_MACH_EXCEPTIONS 1\n", + } +); + +#---------------------------------------------------------------------- +# Open the config file +#---------------------------------------------------------------------- +open(CONFIG, "> $config_file") || die "Couldn't open '$config_file' for writing: $!\n"; +print CONFIG "/*" . "-" x 72 . "\n"; +print CONFIG "// This file is auto generated by a config.pl, do not edit by hand!\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// COMMAND LINE\n"; +print CONFIG "// " . join(' ', @ARGV) . "\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// ENVIRONMENT\n"; +my $key; +my $val; +while (($key, $val) = each %ENV) +{ + printf CONFIG "// %s = %s\n", $key, $val; +} +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// SETTINGS\n"; +print CONFIG "// config_file: '$config_file'\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "*/\n\n"; +print CONFIG "#ifndef liblldb_PDConfig_h_\n"; +print CONFIG "#define liblldb_PDConfig_h_\n"; + + +#---------------------------------------------------------------------- +# Run the tests +#---------------------------------------------------------------------- +foreach my $test_href (@config_tests) +{ + if (exists $test_href->{COMMENT}) { + print CONFIG "\n$test_href->{COMMENT}\n"; + } else { + print CONFIG "\n// $test_href->{NAME}\n"; + } + + my $test_result = eval "$test_href->{TEST}"; + if ($test_result != 0) + { + print CONFIG "$test_href->{SUCCESS}\n"; + } + else + { + print CONFIG "$test_href->{FAIL}\n"; + } +} + +print CONFIG "#endif // #ifndef liblldb_PDConfig_h_\n"; +close(CONFIG); + diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl b/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl new file mode 100755 index 000000000000..96b3115c9125 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl @@ -0,0 +1,409 @@ +#!/usr/bin/perl + +use strict; +use Cwd 'abs_path'; +our $home = $ENV{HOME} || die "ERROR: Couldn't deduce your home directory...\n"; + +our @inc_paths = ( + './include', +); + +my $inc_paths_added = 0; +foreach my $inc_path (@inc_paths) +{ + if (-e $inc_path) + { + push (@INC, abs_path($inc_path)); + $inc_paths_added++; + } +} + +if ($inc_paths_added == 0) +{ + die "Please compile the Release version of lldb\n"; +} + +require lldb; + +# my $state = lldb::eStateAttaching; + +use constant UINT32_MAX => 4294967295; + +#---------------------------------------------------------------------- +# Interactive Commands +#---------------------------------------------------------------------- +our %commands = ( + break => { + name => 'break', # in case an alias is used to get to this command + description => "Sets a breakpoint.", + usage => ["break ADDR"], + function => \&command_set_breakpoint, + runs_target => 0, + }, + delete => { + name => 'delete', # in case an alias is used to get to this command + description => "Deletes one or more breakpoints by ID.\ +If no breakpoint IDs are given all breakpoints will be deleted.\ +If one or more IDs are given, only those breakpoints will be deleted.", + usage => ["delete [ID1 ID2 ...]"], + function => \&command_clear_breakpoint, + runs_target => 0, + }, + continue => { + name => 'continue', # in case an alias is used to get to this command + description => "Continues target execution.", + usage => ["continue [ADDR]"], + function => \&command_continue, + runs_target => 1 + }, + step => { + name => 'step', # in case an alias is used to get to this command + description => "Single steps one instruction.", + usage => ["step"], + function => \&command_step, + runs_target => 1 + }, + info => { + name => 'info', # in case an alias is used to get to this command + description => "Gets info on a variety of things.", + usage => ["info reg", "info thread", "info threads"], + function => \&command_info, + runs_target => 0 + }, + help => { + name => 'help', # in case an alias is used to get to this command + description => "Displays a list of all commands, or help for a specific command.", + usage => ["help", "help CMD"], + function => \&command_help, + runs_target => 0 + } +); + +#---------------------------------------------------------------------- +# Command aliases +#---------------------------------------------------------------------- +our %aliases = ( + b => $commands{break}, + c => $commands{continue}, + s => $commands{step}, + d => $commands{delete}, + h => $commands{help} +); + +our $opt_g = 0; # Enable verbose debug logging +our $opt_v = 0; # Verbose mode +my $prev_command_href = undef; +my $stdio = '/dev/stdin'; +my $launch = 0; +my @env = (); +my @break_ids; + +#---------------------------------------------------------------------- +# Given a command string, return the command hash reference for it, or +# undef if it doesn't exist. +#---------------------------------------------------------------------- +sub get_command_hash_ref +{ + my $cmd = shift; + my $cmd_href = undef; + if (length($cmd) == 0) { $cmd_href = $prev_command_href; } + elsif (exists $aliases{$cmd}) { $cmd_href = $aliases{$cmd}; } + elsif (exists $commands{$cmd}) { $cmd_href = $commands{$cmd}; } + defined $cmd_href and $prev_command_href = $cmd_href; + return $cmd_href; +} + +#---------------------------------------------------------------------- +# Set a breakpoint +#---------------------------------------------------------------------- +sub command_set_breakpoint +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_set_breakpoint (pid = $pid, locations = @_)\n"; + foreach my $location (@_) + { + my $success = 0; + my $address = hex($location); + if ($address != 0) + { + my $break_id = lldb::PDBreakpointSet ($pid, $address, 1, 0); + if ($break_id != $lldb::PD_INVALID_BREAK_ID) + { + printf("Breakpoint %i is set.\n", $break_id); + push(@break_ids, $break_id); + $success = 1; + } + } + $success or print("error: failed to set breakpoint at $location.\n"); + } + return 1; +} + +#---------------------------------------------------------------------- +# Clear a breakpoint +#---------------------------------------------------------------------- +sub command_clear_breakpoint +{ + my $pid = shift; + my $tid = shift; + if (@_) + { + my $break_id; + my @cleared_break_ids; + my @new_break_ids; + $opt_g and print "command_clear_breakpoint (pid = $pid, break_ids = @_)\n"; + foreach $break_id (@_) + { + if (lldb::PDBreakpointClear ($pid, $break_id)) + { + printf("Breakpoint %i has been cleared.\n", $break_id); + push (@cleared_break_ids, $break_id); + } + else + { + printf("error: failed to clear breakpoint %i.\n", $break_id); + } + } + + foreach my $old_break_id (@break_ids) + { + my $found_break_id = 0; + foreach $break_id (@cleared_break_ids) + { + if ($old_break_id == $break_id) + { + $found_break_id = 1; + } + } + $found_break_id or push (@new_break_ids, $old_break_id); + } + @break_ids = @new_break_ids; + } + else + { + # Nothing specified, clear all breakpoints + return command_clear_breakpoint($pid, $tid, @break_ids); + } + return 1; +} +#---------------------------------------------------------------------- +# Continue program execution +#---------------------------------------------------------------------- +sub command_continue +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_continue (pid = $pid)\n"; + if ($pid != $lldb::PD_INVALID_PROCESS_ID) + { + $opt_v and printf("Resuming pid %d...\n", $pid); + return lldb::PDProcessResume ($pid); + } + return 0; +} + +sub command_step +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_step (pid = $pid, tid = $tid)\n"; + if ($pid != $lldb::PD_INVALID_PROCESS_ID) + { + $opt_v and printf("Single stepping pid %d tid = %4.4x...\n", $pid, $tid); + return lldb::PDThreadResume ($pid, $tid, 1); + } + return 0; +} + +sub command_info +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_step (pid = $pid, tid = $tid)\n"; + if ($pid != $lldb::PD_INVALID_PROCESS_ID) + { + if (@_) + { + my $info_cmd = shift; + if ($info_cmd eq 'reg') + { + + } + elsif ($info_cmd eq 'thread') + { + # info on the current thread + printf("thread 0x%4.4x %s\n", $tid, lldb::PDThreadGetInfo($pid, $tid)); + } + elsif ($info_cmd eq 'threads') + { + my $num_threads = lldb::PDProcessGetNumThreads( $pid ); + for my $thread_num (1..$num_threads) + { + my $curr_tid = lldb::PDProcessGetThreadAtIndex ( $pid, $thread_num - 1 ); + printf("%c%u - thread 0x%4.4x %s\n", $curr_tid == $tid ? '*' : ' ', $thread_num, $curr_tid, lldb::PDThreadGetInfo($pid, $curr_tid)); + } + } + } + } + return 1; +} +#---------------------------------------------------------------------- +# Get help on all commands, or a specific list of commands +#---------------------------------------------------------------------- +sub command_help +{ + my $pid = shift; + my $tid = shift; + if (@_) + { + $opt_g and print "command_continue (pid = $pid, commands = @_)\n"; + foreach my $cmd (@_) + { + my $cmd_href = get_command_hash_ref($cmd); + if ($cmd_href) + { + print '#', '-' x 72, "\n# $cmd_href->{name}\n", '#', '-' x 72, "\n"; + my $usage_aref = $cmd_href->{usage}; + if (@{$usage_aref}) + { + print " USAGE\n"; + foreach my $usage (@{$usage_aref}) { + print " $usage\n"; + } + print "\n"; + } + print " DESCRIPTION\n $cmd_href->{description}\n\n"; + } + else + { + print " invalid command: '$cmd'\n\n"; + } + } + } + else + { + return command_help($pid, sort keys %commands); + } + return 1; +} + + +#lldb::PDLogSetLogMask ($lldb::PD_LOG_ALL); +#lldb::PDLogSetLogFile ('/dev/stdout'); + +print "running: ", join(' ', @ARGV), "\n"; + +my $pid = lldb::PDProcessLaunch ($ARGV[0], \@ARGV, \@env, "i386", '/dev/stdin', '/dev/stdout', '/dev/stderr', $launch, '', 0); +my $pid_state; +while ($pid) +{ + $opt_g and printf("PDProcessWaitForEvents (%d, 0x%4.4x, SET, 1)\n", $pid, $lldb::PD_ALL_EVENTS); + my $events = lldb::PDProcessWaitForEvents ($pid, $lldb::PD_ALL_EVENTS, 1, 1); + if ($events) + { + $opt_g and printf ("Got event: 0x%8.8x\n", $events); + + if ($events & $lldb::PD_EVENT_IMAGES_CHANGED) + { + $opt_g and printf("pid %d images changed...\n", $pid); + } + + if ($events & $lldb::PD_EVENT_STDIO) + { + $opt_g and printf("pid %d has stdio...\n", $pid); + } + + if ($events & $lldb::PD_EVENT_ASYNC_INTERRUPT) + { + $opt_g and printf("pid %d got async interrupt...\n", $pid); + } + + if ($events & $lldb::PD_EVENT_RUNNING) + { + $pid_state = lldb::PDProcessGetState ($pid); + $opt_v and printf( "pid %d state: %s.\n", $pid, lldb::PDStateAsString ($pid_state) ); + } + + if ($events & $lldb::PD_EVENT_STOPPED) + { + $pid_state = lldb::PDProcessGetState ($pid); + $opt_v and printf( "pid %d state: %s.\n", $pid, lldb::PDStateAsString ($pid_state) ); + + if ($pid_state == $lldb::eStateUnloaded || + $pid_state == $lldb::eStateAttaching || + $pid_state == $lldb::eStateLaunching ) + { + + } + elsif ( $pid_state == $lldb::eStateStopped ) + { + my $tid = lldb::PDProcessGetCurrentThread ( $pid ); + my $pc = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "eip", 0); + $pc != 0 and printf("pc = 0x%8.8x ", $pc); + # my $sp = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "esp", 0); + # $sp != 0 and printf("sp = 0x%8.8x ", $sp); + # my $fp = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "ebp", 0); + # $sp != 0 and printf("fp = 0x%8.8x ", $fp); + # print "\n"; + my $done = 0; + my $input; + while (!$done) + { + print '(pdbg) '; + + chomp($input = ); + my @argv = split(/\s+/, $input); + my $cmd = @argv ? shift @argv : undef; + my $cmd_href = get_command_hash_ref ($cmd); + if ($cmd_href) + { + # Print the expanded alias if one was used + if ($opt_v and $cmd_href->{name} ne $cmd) + { + print "$cmd_href->{name} @argv\n"; + } + + # Call the command's callback function to make things happen + if ($cmd_href->{function}($pid, $tid, @argv)) + { + $done = $cmd_href->{runs_target}; + } + } + else + { + print "invalid command: '$cmd'\nType 'help' for a list of all commands.\nType 'help CMD' for help on a specific commmand.\n"; + } + } + } + elsif ( $pid_state == $lldb::eStateRunning || + $pid_state == $lldb::eStateStepping ) + { + + } + elsif ( $pid_state == $lldb::eStateCrashed || + $pid_state == $lldb::eStateDetached || + $pid_state == $lldb::eStateExited ) + { + $pid = 0; + } + elsif ( $pid_state == $lldb::eStateSuspended ) + { + } + else + { + } + } + + if ($pid) + { + $opt_g and printf("PDProcessResetEvents(%d, 0x%8.8x)\n", $pid, $events); + lldb::PDProcessResetEvents($pid, $events); + } + } +} + +if ($pid != $lldb::PD_INVALID_PROCESS_ID) +{ + lldb::PDProcessDetach ($pid); +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp new file mode 100644 index 000000000000..7dc8d2ce652e --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp @@ -0,0 +1,575 @@ +//===-- MachException.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 "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" + +#include "MachException.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb_private; + +// Routine mach_exception_raise +extern "C" +kern_return_t catch_mach_exception_raise +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt +); + +extern "C" +kern_return_t catch_mach_exception_raise_state +( + mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +// Routine mach_exception_raise_state_identity +extern "C" +kern_return_t catch_mach_exception_raise_state_identity +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +extern "C" boolean_t mach_exc_server( + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + +// Any access to the g_message variable should be done by locking the +// g_message_mutex first, using the g_message variable, then unlocking +// the g_message_mutex. See MachException::Message::CatchExceptionRaise() +// for sample code. + +static MachException::Data *g_message = NULL; +//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER; + + +extern "C" +kern_return_t +catch_mach_exception_raise_state +( + mach_port_t exc_port, + exception_type_t exc_type, + const mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt +) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + { + log->Printf("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)", + __FUNCTION__, + exc_port, + exc_type, MachException::Name(exc_type), + exc_data, + exc_data_count); + } + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise_state_identity +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +) +{ + kern_return_t kret; + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + { + log->Printf("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, + exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); + } + kret = mach_port_deallocate (mach_task_self (), task_port); + kret = mach_port_deallocate (mach_task_self (), thread_port); + + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count) +{ + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + { + log->Printf("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, + exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); + } + + g_message->task_port = task_port; + g_message->thread_port = thread_port; + g_message->exc_type = exc_type; + g_message->exc_data.resize(exc_data_count); + ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t)); + return KERN_SUCCESS; +} + + +void +MachException::Message::PutToLog(Log *log) const +{ + if (log) + { + log->Printf(" exc_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx } ", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id); + + log->Printf( "reply_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx }", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id); + state.PutToLog(log); + } +} + +bool +MachException::Data::GetStopInfo(Thread::StopInfo *stop_info) const +{ + // Zero out the structure. + stop_info->Clear(); + + // Make sure we have a valid exception before we return anything valid + if (exc_type == 0) + return true; + // We always stop with a mach exceptions + const size_t exc_data_count = exc_data.size(); + stop_info->SetStopReasonWithException(exc_type, exc_data_count); + + // Fill in a text description + const char * exc_name = MachException::Name(exc_type); + StreamString sstr; + if (exc_name) + sstr.PutCString(exc_name); + else + sstr.Printf ("%i", exc_type); + + int signal = SoftSignal(); + if (signal > 0) + { + const char *sig_str = Host::GetSignalAsCString(signal); + if (sig_str) + sstr.Printf (" EXC_SOFT_SIGNAL(%s)", sig_str); + else + sstr.Printf (" EXC_SOFT_SIGNAL(%i)", signal); + } + else + { + // No special disassembly for exception data, just + sstr.Printf (" data[%zu] = {", exc_data_count); + + for (size_t idx = 0; idx < exc_data_count; ++idx) + sstr.Printf (MACH_EXCEPTION_DATA_FMT_MINHEX "%s", exc_data[idx], ((idx + 1 == exc_data_count) ? "" : ",")); + + sstr.PutChar('}'); + } + + stop_info->SetStopDescription (sstr.GetData()); + + // Copy the exception data + size_t i; + for (i=0; iSetExceptionDataAtIndex(i, exc_data[i]); + + return true; +} + + +void +MachException::Data::DumpStopReason() const +{ + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(); + if (log) + { + int signal = SoftSignal(); + if (signal > 0) + { + const char *signal_str = Host::GetSignalAsCString(signal); + if (signal_str) + log->Printf ("signal(%s)", signal_str); + else + log->Printf ("signal(%i)", signal); + return; + } + log->Printf ("%s", Name(exc_type)); + } +} + +kern_return_t +MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port) +{ + Error err; + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0; + if (log && ((options & MACH_RCV_TIMEOUT) == 0)) + { + // Dump this log message if we have no timeout in case it never returns + log->Printf ("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + 0, + sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + + err = ::mach_msg (&exc_msg.hdr, + options, // options + 0, // Send size + sizeof (exc_msg.data), // Receive size + port, // exception port to watch for exception on + mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter) + notify_port); + + // Dump any errors we get + if (log && err.GetError() != MACH_RCV_TIMED_OUT) + { + log->Error("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + 0, + sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + return err.GetError(); +} + +bool +MachException::Message::CatchExceptionRaise() +{ + bool success = false; + // locker will keep a mutex locked until it goes out of scope +// Mutex::Locker locker(&g_message_mutex); + // log->Printf ("calling mach_exc_server"); + g_message = &state; + // The exc_server function is the MIG generated server handling function + // to handle messages from the kernel relating to the occurrence of an + // exception in a thread. Such messages are delivered to the exception port + // set via thread_set_exception_ports or task_set_exception_ports. When an + // exception occurs in a thread, the thread sends an exception message to + // its exception port, blocking in the kernel waiting for the receipt of a + // reply. The exc_server function performs all necessary argument handling + // for this kernel message and calls catch_exception_raise, + // catch_exception_raise_state or catch_exception_raise_state_identity, + // which should handle the exception. If the called routine returns + // KERN_SUCCESS, a reply message will be sent, allowing the thread to + // continue from the point of the exception; otherwise, no reply message + // is sent and the called routine must have dealt with the exception + // thread directly. + if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr)) + { + success = true; + } + else + { + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + log->Printf ("mach_exc_server returned zero..."); + } + g_message = NULL; + return success; +} + + + +kern_return_t +MachException::Message::Reply(task_t task, pid_t pid, int signal) +{ + // Reply to the exception... + Error err; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(); + if (log) + log->Printf("MachException::Message::Reply (task = 0x%4.4x, pid = %i, signal = %i)", task, pid, signal); + + // If we had a soft signal, we need to update the thread first so it can + // continue without signaling + int soft_signal = state.SoftSignal(); + int state_pid = LLDB_INVALID_PROCESS_ID; + if (task == state.task_port) + { + // This is our task, so we can update the signal to send to it + state_pid = pid; + } + else + { + err = ::pid_for_task(state.task_port, &state_pid); + } + + if (signal == LLDB_INVALID_SIGNAL_NUMBER) + signal = 0; + + if (log) + log->Printf("MachException::Message::Reply () updating thread signal to %i (original soft_signal = %i)", signal, soft_signal); + + if (state_pid != LLDB_INVALID_PROCESS_ID) + { + errno = 0; + if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, signal) != 0) + { + if (soft_signal != LLDB_INVALID_SIGNAL_NUMBER) + // We know we currently can't forward signals for threads that didn't stop in EXC_SOFT_SIGNAL... + // So only report it as an error if we should have been able to do it. + err.SetErrorToErrno(); + else + err.Clear(); + } + else + err.Clear(); + + if (log && log->GetMask().IsSet(PD_LOG_EXCEPTIONS) || err.Fail()) + err.PutToLog(log, "::ptrace (request = PT_THUPDATE, pid = %i, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, signal); + } + + err = ::mach_msg ( &reply_msg.hdr, + MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply_msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (log) + log->LogIf (PD_LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x) = 0x%8.8x", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id, + MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply_msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL, + err.GetError()); + + + if (err.Fail()) + { + if (err.GetError() == MACH_SEND_INTERRUPTED) + { + err.PutToLog(log, "::mach_msg() - send interrupted"); + } + else + { + if (state.task_port == task) + { + err.PutToLog(log, "::mach_msg() - failed (task)"); + abort (); + } + else + { + err.PutToLog(log, "::mach_msg() - failed (child of task)"); + } + } + } + + return err.GetError(); +} + + +void +MachException::Data::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + const char *exc_type_name = MachException::Name(exc_type); + + log->Printf (" state { task_port = 0x%4.4x, thread_port = 0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???"); + + const size_t exc_data_count = exc_data.size(); + // Dump any special exception data contents + int soft_signal = SoftSignal(); + if (soft_signal > 0) + { + const char *sig_str = Host::GetSignalAsCString(soft_signal); + log->Printf (" exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal"); + } + else + { + // No special disassembly for this data, just dump the data + size_t idx; + for (idx = 0; idx < exc_data_count; ++idx) + { + log->Printf(" exc_data[%u]: " MACH_EXCEPTION_DATA_FMT_HEX, idx, exc_data[idx]); + } + } +} + + +MachException::PortInfo::PortInfo() : + count(0) +{ + ::bzero (masks, sizeof(masks)); + ::bzero (ports, sizeof(ports)); + ::bzero (behaviors, sizeof(behaviors)); + ::bzero (flavors, sizeof(flavors)); +} + + +kern_return_t +MachException::PortInfo::Save (task_t task) +{ + count = EXC_TYPES_COUNT; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + if (log) + log->Printf ("MachException::PortInfo::Save (task = 0x%4.4x)", task); + Error err; + if (log) + log->Printf("::task_get_exception_ports (task=0x%4.4x, mask=0x%x, maskCnt<=>%u, ports, behaviors, flavors)...", task, EXC_MASK_ALL, count); + err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors); + if (log || err.Fail()) + err.PutToLog(log, "::task_get_exception_ports (task=0x%4.4x, mask=0x%x, maskCnt<=>%u, ports, behaviors, flavors)", task, EXC_MASK_ALL, count); + if (log) + { + mach_msg_type_number_t i; + log->Printf("Index Mask Port Behavior Flavor", masks[i], ports[i], behaviors[i], flavors[i]); + log->Printf("===== -------- -------- -------- --------"); + for (i=0; iPrintf("[%3u] %8.8x %8.8x %8.8x %8.8x", i, masks[i], ports[i], behaviors[i], flavors[i]); + } + if (err.Fail()) + count = 0; + return err.GetError(); +} + +kern_return_t +MachException::PortInfo::Restore (task_t task) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf("MachException::PortInfo::Restore (task = 0x%4.4x)", task); + uint32_t i = 0; + Error err; + if (count > 0) + { + for (i = 0; i < count; i++) + { + err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]); + if (log || err.Fail()) + err.PutToLog(log, "::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]); + + if (err.Fail()) + break; + } + } + count = 0; + return err.GetError(); +} + +const char * +MachException::Name(exception_type_t exc_type) +{ + switch (exc_type) + { + case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS"; + case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION"; + case EXC_ARITHMETIC: return "EXC_ARITHMETIC"; + case EXC_EMULATION: return "EXC_EMULATION"; + case EXC_SOFTWARE: return "EXC_SOFTWARE"; + case EXC_BREAKPOINT: return "EXC_BREAKPOINT"; + case EXC_SYSCALL: return "EXC_SYSCALL"; + case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL"; + case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; +#ifdef EXC_CRASH + case EXC_CRASH: return "EXC_CRASH"; +#endif + default: + break; + } + return NULL; +} + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h new file mode 100644 index 000000000000..1f3aeb07b0aa --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h @@ -0,0 +1,148 @@ +//===-- MachException.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_MachException_h_ +#define liblldb_MachException_h_ + +#include +#include +#include "lldb/lldb-private.h" +#include "lldb/Target/Thread.h" +// TODO: Get the config script to run to this plug-in +//#include "PDConfig.h" +#define HAVE_64_BIT_MACH_EXCEPTIONS // REMOVE THIS WHEN PDConfig.h is included above +#ifdef HAVE_64_BIT_MACH_EXCEPTIONS + +#define MACH_EXCEPTION_DATA_FMT_DEC "%lld" +#define MACH_EXCEPTION_DATA_FMT_HEX "0x%16.16llx" +#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%llx" + +#else + +#define MACH_EXCEPTION_DATA_FMT_DEC "%d" +#define MACH_EXCEPTION_DATA_FMT_HEX "0x%8.8x" +#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%x" + +#endif + +class MachProcess; + +typedef union MachMessageTag +{ + mach_msg_header_t hdr; + char data[1024]; +} MachMessage; + + +class MachException +{ +public: + + struct PortInfo + { + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; + mach_msg_type_number_t count; + + PortInfo(); + kern_return_t Save(task_t task); + kern_return_t Restore(task_t task); + }; + + struct Data + { + task_t task_port; + lldb::tid_t thread_port; + exception_type_t exc_type; + std::vector exc_data; + Data() : + task_port(TASK_NULL), + thread_port(THREAD_NULL), + exc_type(0), + exc_data() + { + } + + void Clear() + { + task_port = TASK_NULL; + thread_port = THREAD_NULL; + exc_type = 0; + exc_data.clear(); + } + bool IsValid() const + { + return task_port != TASK_NULL && + thread_port != THREAD_NULL && + exc_type != 0; + } + // Return the SoftSignal for this MachException data, or zero if there is none + int SoftSignal() const + { + if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL) + return exc_data[1]; + return LLDB_INVALID_SIGNAL_NUMBER; + } + bool IsBreakpoint() const + { + return (exc_type == EXC_BREAKPOINT) || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1); + } + void PutToLog(lldb_private::Log *log) const; + void DumpStopReason() const; + bool GetStopInfo(lldb_private::Thread::StopInfo *stop_info) const; + }; + + struct Message + { + MachMessage exc_msg; + MachMessage reply_msg; + Data state; + + Message() : + exc_msg(), + reply_msg(), + state() + { + memset(&exc_msg, 0, sizeof(exc_msg)); + memset(&reply_msg, 0, sizeof(reply_msg)); + } + bool CatchExceptionRaise(); + void PutToLog(lldb_private::Log *log) const; + kern_return_t Reply (task_t task, pid_t pid, int signal); + kern_return_t Receive( mach_port_t receive_port, + mach_msg_option_t options, + mach_msg_timeout_t timeout, + mach_port_t notify_port = MACH_PORT_NULL); + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + enum + { + e_actionForward, // Forward signal to inferior process + e_actionStop, // Stop when this signal is received + }; + struct Action + { + task_t task_port; // Set to TASK_NULL for any TASK + lldb::tid_t thread_port; // Set to THREAD_NULL for any thread + exception_type_t exc_mask; // Mach exception mask to watch for + std::vector exc_data_mask; // Mask to apply to exception data, or empty to ignore exc_data value for exception + std::vector exc_data_value; // Value to compare to exception data after masking, or empty to ignore exc_data value for exception + uint8_t flags; // Action flags describing what to do with the exception + }; + static const char *Name(exception_type_t exc_type); +}; + +#endif diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp new file mode 100644 index 000000000000..1a0c3c489972 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp @@ -0,0 +1,674 @@ +//===-- MachTask.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachTask.h" + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#if defined (__arm__) + +#include +#include +#include + +#endif + +#include "lldb/Host/Host.h" +#include "lldb/Core/DataExtractor.h" + +// Project includes +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" + + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// MachTask constructor +//---------------------------------------------------------------------- +MachTask::MachTask(ProcessMacOSX *process) : + m_process (process), + m_task (TASK_NULL), + m_vm_memory (), + m_exception_thread (0), + m_exception_port (MACH_PORT_NULL), + m_exc_port_info() +{ + memset(&m_exc_port_info, 0, sizeof(m_exc_port_info)); + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +MachTask::~MachTask() +{ + Clear(); +} + + +//---------------------------------------------------------------------- +// MachTask::Suspend +//---------------------------------------------------------------------- +kern_return_t +MachTask::Suspend() +{ + Error err; + task_t task = GetTaskPort(); + err = ::task_suspend (task); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + err.PutToLog(log, "::task_suspend ( target_task = 0x%4.4x )", task); + return err.GetError(); +} + + +//---------------------------------------------------------------------- +// MachTask::Resume +//---------------------------------------------------------------------- +kern_return_t +MachTask::Resume() +{ + Error err; + task_t task = GetTaskPort(); + err = ::task_resume (task); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + err.PutToLog(log, "::task_resume ( target_task = 0x%4.4x )", task); + return err.GetError(); +} + +int32_t +MachTask::GetSuspendCount () const +{ + struct task_basic_info task_info; + if (BasicInfo(&task_info) == KERN_SUCCESS) + return task_info.suspend_count; + return -1; +} + +//---------------------------------------------------------------------- +// MachTask::ExceptionPort +//---------------------------------------------------------------------- +mach_port_t +MachTask::ExceptionPort() const +{ + return m_exception_port; +} + +//---------------------------------------------------------------------- +// MachTask::ExceptionPortIsValid +//---------------------------------------------------------------------- +bool +MachTask::ExceptionPortIsValid() const +{ + return MACH_PORT_VALID(m_exception_port); +} + + +//---------------------------------------------------------------------- +// MachTask::Clear +//---------------------------------------------------------------------- +void +MachTask::Clear() +{ + // Do any cleanup needed for this task + m_task = TASK_NULL; + m_exception_thread = 0; + m_exception_port = MACH_PORT_NULL; + +} + + +//---------------------------------------------------------------------- +// MachTask::SaveExceptionPortInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::SaveExceptionPortInfo() +{ + return m_exc_port_info.Save(GetTaskPort()); +} + +//---------------------------------------------------------------------- +// MachTask::RestoreExceptionPortInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::RestoreExceptionPortInfo() +{ + return m_exc_port_info.Restore(GetTaskPort()); +} + + +//---------------------------------------------------------------------- +// MachTask::ReadMemory +//---------------------------------------------------------------------- +size_t +MachTask::ReadMemory (lldb::addr_t addr, void *buf, size_t size, Error& error) +{ + size_t n = 0; + task_t task = GetTaskPort(); + if (task != TASK_NULL) + { + n = m_vm_memory.Read(task, addr, buf, size, error); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + if (log) + { + log->Printf ("MachTask::ReadMemory ( addr = 0x%16.16llx, size = %zu, buf = %8.8p) => %u bytes read", (uint64_t)addr, size, buf, n); + if (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_LONG) || (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DataExtractor data((uint8_t*)buf, n, eByteOrderHost, 4); + data.PutToLog(log, 0, n, addr, 16, DataExtractor::TypeUInt8); + } + } + } + return n; +} + + +//---------------------------------------------------------------------- +// MachTask::WriteMemory +//---------------------------------------------------------------------- +size_t +MachTask::WriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error& error) +{ + size_t n = 0; + task_t task = GetTaskPort(); + if (task != TASK_NULL) + { + n = m_vm_memory.Write(task, addr, buf, size, error); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + if (log) + { + log->Printf ("MachTask::WriteMemory ( addr = 0x%16.16llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n); + if (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_LONG) || (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DataExtractor data((uint8_t*)buf, n, eByteOrderHost, 4); + data.PutToLog(log, 0, n, addr, 16, DataExtractor::TypeUInt8); + } + } + } + return n; +} + +//---------------------------------------------------------------------- +// MachTask::AllocateMemory +//---------------------------------------------------------------------- +lldb::addr_t +MachTask::AllocateMemory (size_t size, uint32_t permissions, Error& error) +{ + // FIXME: vm_allocate allocates a page at a time, so we should use + // host_page_size to get the host page size and then parcel out the + // page we get back until it is filled. + // FIXME: Add log messages. + + kern_return_t kret; + mach_vm_address_t addr; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + + kret = ::mach_vm_allocate (GetTaskPort(), &addr, size, TRUE); + if (kret == KERN_SUCCESS) + { + // Set the protections: + vm_prot_t mach_prot = 0; + if (permissions & lldb::ePermissionsReadable) + mach_prot |= VM_PROT_READ; + if (permissions & lldb::ePermissionsWritable) + mach_prot |= VM_PROT_WRITE; + if (permissions & lldb::ePermissionsExecutable) + mach_prot |= VM_PROT_EXECUTE; + + kret = ::mach_vm_protect (GetTaskPort(), addr, size, 0, mach_prot); + if (kret == KERN_SUCCESS) + { + if (log) + log->Printf("Allocated memory at addr = 0x%16.16llx, size = %zu, prot = 0x%x)", (uint64_t) addr, size, mach_prot); + m_allocations.insert (std::make_pair(addr, size)); + return (lldb::addr_t) addr; + } + else + { + if (log) + log->Printf("Failed to set protections on memory at addr = 0x%16.16llx, size = %zu), prot = 0x%x", (uint64_t) addr, size, mach_prot); + kret = ::mach_vm_deallocate (GetTaskPort(), addr, size); + return LLDB_INVALID_ADDRESS; + } + } + else + { + if (log) + log->Printf("Failed to set allocate memory: size = %zu)", size); + return LLDB_INVALID_ADDRESS; + } +} + +//---------------------------------------------------------------------- +// MachTask::DeallocateMemory +//---------------------------------------------------------------------- +Error +MachTask::DeallocateMemory (lldb::addr_t ptr) +{ + Error error; + // We have to stash away sizes for the allocations... + allocation_collection::iterator pos, end = m_allocations.end(); + for (pos = m_allocations.begin(); pos != end; pos++) + { + if ((*pos).first == ptr) + { + m_allocations.erase (pos); + error = ::mach_vm_deallocate (GetTaskPort(), (vm_address_t) ptr, (*pos).second); + return error; + } + } + error.SetErrorStringWithFormat("no memory allocated at 0x%llx", (uint64_t)ptr); + return error; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::GetTaskPortForProcessID (Error &err) +{ + err.Clear(); + if (m_task == TASK_NULL && m_process != NULL) + m_task = MachTask::GetTaskPortForProcessID(m_process->GetID(), err); + return m_task; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::GetTaskPortForProcessID (lldb::pid_t pid, Error &err) +{ + task_t task = TASK_NULL; + if (pid != LLDB_INVALID_PROCESS_ID) + { + mach_port_t task_self = mach_task_self (); + err = ::task_for_pid ( task_self, pid, &task); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + { + err.PutToLog(log, "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, task => 0x%4.4x ) %u/%u %u/%u", task_self, pid, task, getuid(), geteuid(), getgid(), getegid()); + } + } + return task; +} + + +//---------------------------------------------------------------------- +// MachTask::BasicInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::BasicInfo(struct task_basic_info *info) const +{ + return BasicInfo (GetTaskPort(), info); +} + +//---------------------------------------------------------------------- +// MachTask::BasicInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::BasicInfo(task_t task, struct task_basic_info *info) +{ + if (info == NULL) + return KERN_INVALID_ARGUMENT; + + Error err; + mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; + err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + err.PutToLog(log, "::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE) && err.Success()) + { + float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; + float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; + log->Printf ("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8x, resident_size = 0x%8.8x, user_time = %f, system_time = %f }", + info->suspend_count, info->virtual_size, info->resident_size, user, system); + } + return err.GetError(); +} + + +//---------------------------------------------------------------------- +// MachTask::IsValid +// +// Returns true if a task is a valid task port for a current process. +//---------------------------------------------------------------------- +bool +MachTask::IsValid () const +{ + return MachTask::IsValid(GetTaskPort()); +} + +//---------------------------------------------------------------------- +// MachTask::IsValid +// +// Returns true if a task is a valid task port for a current process. +//---------------------------------------------------------------------- +bool +MachTask::IsValid (task_t task) +{ + if (task != TASK_NULL) + { + struct task_basic_info task_info; + return BasicInfo(task, &task_info) == KERN_SUCCESS; + } + return false; +} + + +bool +MachTask::StartExceptionThread(Error &err) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + + if (log) + log->Printf ("MachTask::%s ( )", __FUNCTION__); + task_t task = GetTaskPortForProcessID(err); + if (MachTask::IsValid(task)) + { + // Got the mach port for the current process + mach_port_t task_self = mach_task_self (); + + // Allocate an exception port that we will use to track our child process + err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port); + if (log || err.Fail()) + err.PutToLog(log, "::mach_port_allocate (task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, &m_exception_port => 0x%4.4x)", + task_self, m_exception_port); + if (err.Fail()) + return false; + + // Add the ability to send messages on the new exception port + err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND); + if (log || err.Fail()) + err.PutToLog(log, "::mach_port_insert_right (task_self=0x%4.4x, m_exception_port=0x%4.4x, m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND)", + task_self, m_exception_port, m_exception_port); + if (err.Fail()) + return false; + + // Save the original state of the exception ports for our child process + err = SaveExceptionPortInfo(); + + // Set the ability to get all exceptions on this port + err = ::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); + if (log || err.Fail()) + err.PutToLog(log, "::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE)"); + if (err.Fail()) + return false; + + // Create the exception thread + char thread_name[256]; + ::snprintf (thread_name, sizeof(thread_name), "", m_process->GetID()); + m_exception_thread = Host::ThreadCreate (thread_name, MachTask::ExceptionThread, this, &err); + + return err.Success(); + } + return false; +} + +kern_return_t +MachTask::ShutDownExceptionThread() +{ + Error err; + + if (m_exception_thread == NULL) + return KERN_SUCCESS; + + err = RestoreExceptionPortInfo(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + + // NULL our our exception port and let our exception thread exit + mach_port_t exception_port = m_exception_port; + m_exception_port = NULL; + + Host::ThreadCancel (m_exception_thread, &err); + if (log || err.Fail()) + err.PutToLog(log, "Host::ThreadCancel ( thread = %p )", m_exception_thread); + + Host::ThreadJoin (m_exception_thread, NULL, &err); + if (log || err.Fail()) + err.PutToLog(log, "Host::ThreadJoin ( thread = %p, result_ptr = NULL)", m_exception_thread); + + // Deallocate our exception port that we used to track our child process + mach_port_t task_self = mach_task_self (); + err = ::mach_port_deallocate (task_self, exception_port); + if (log || err.Fail()) + err.PutToLog(log, "::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port); + exception_port = NULL; + + Clear(); + return err.GetError(); +} + + +void * +MachTask::ExceptionThread (void *arg) +{ + if (arg == NULL) + return NULL; + + MachTask *mach_task = (MachTask*) arg; + ProcessMacOSX *mach_proc = mach_task->Process(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + if (log) + log->Printf ("MachTask::%s (arg = %p) thread starting...", __FUNCTION__, arg); + + // We keep a count of the number of consecutive exceptions received so + // we know to grab all exceptions without a timeout. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main loop in this + // thread can stop periodically if needed to service things related to this + // process. + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + uint32_t num_exceptions_received = 0; + Error err; + task_t task = mach_task->GetTaskPort(); + mach_msg_timeout_t periodic_timeout = 1000; + +#if defined (__arm__) + mach_msg_timeout_t watchdog_elapsed = 0; + mach_msg_timeout_t watchdog_timeout = 60 * 1000; + lldb::pid_t pid = mach_proc->GetID(); + CFReleaser watchdog; + + if (mach_proc->ProcessUsingSpringBoard()) + { + // Request a renewal for every 60 seconds if we attached using SpringBoard + watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60)); + if (log) + log->Printf ("::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get()); + + if (watchdog.get()) + { + ::SBSWatchdogAssertionRenew (watchdog.get()); + + CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get()); + if (log) + log->Printf ("::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval); + if (watchdogRenewalInterval > 0.0) + { + watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000; + if (watchdog_timeout > 3000) + watchdog_timeout -= 1000; // Give us a second to renew our timeout + else if (watchdog_timeout > 1000) + watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout + } + } + if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout) + periodic_timeout = watchdog_timeout; + } +#endif // #if defined (__arm__) + + while (mach_task->ExceptionPortIsValid()) + { + //::pthread_testcancel (); + + MachException::Message exception_message; + + + if (num_exceptions_received > 0) + { + // No timeout, just receive as many exceptions as we can since we already have one and we want + // to get all currently available exceptions for this task + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0); + } + else if (periodic_timeout > 0) + { + // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms) + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout); + } + else + { + // We don't need to parse all current exceptions or stop periodically, + // just wait for an exception forever. + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0); + } + + if (err.GetError() == MACH_RCV_INTERRUPTED) + { + // If we have no task port we should exit this thread + if (!mach_task->ExceptionPortIsValid()) + { + if (log) + log->Printf ("thread cancelled..."); + break; + } + + // Make sure our task is still valid + if (MachTask::IsValid(task)) + { + // Task is still ok + if (log) + log->Printf ("interrupted, but task still valid, continuing..."); + continue; + } + else + { + if (log) + log->Printf ("task has exited..."); + mach_proc->SetPrivateState (eStateExited); + // Our task has died, exit the thread. + break; + } + } + else if (err.GetError() == MACH_RCV_TIMED_OUT) + { + if (num_exceptions_received > 0) + { + // We were receiving all current exceptions with a timeout of zero + // it is time to go back to our normal looping mode + num_exceptions_received = 0; + + // Notify our main thread we have a complete exception message + // bundle available. + mach_proc->ExceptionMessageBundleComplete(); + + // in case we use a timeout value when getting exceptions... + // Make sure our task is still valid + if (MachTask::IsValid(task)) + { + // Task is still ok + if (log) + log->Printf ("got a timeout, continuing..."); + continue; + } + else + { + if (log) + log->Printf ("task has exited..."); + mach_proc->SetPrivateState (eStateExited); + // Our task has died, exit the thread. + break; + } + continue; + } + +#if defined (__arm__) + if (watchdog.get()) + { + watchdog_elapsed += periodic_timeout; + if (watchdog_elapsed >= watchdog_timeout) + { + LogIf(PD_LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get()); + ::SBSWatchdogAssertionRenew (watchdog.get()); + watchdog_elapsed = 0; + } + } +#endif + } + else if (err.GetError() != KERN_SUCCESS) + { + if (log) + log->Printf ("got some other error, do something about it??? nah, continuing for now..."); + // TODO: notify of error? + } + else + { + if (exception_message.CatchExceptionRaise()) + { + ++num_exceptions_received; + mach_proc->ExceptionMessageReceived(exception_message); + } + } + } + +#if defined (__arm__) + if (watchdog.get()) + { + // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we + // all are up and running on systems that support it. The SBS framework has a #define + // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now + // so it should still build either way. + LogIf(PD_LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get()); + ::SBSWatchdogAssertionRelease (watchdog.get()); + } +#endif // #if defined (__arm__) + + if (log) + log->Printf ("MachTask::%s (arg = %p) thread exiting...", __FUNCTION__, arg); + return NULL; +} + +lldb::addr_t +MachTask::GetDYLDAllImageInfosAddress () +{ +#ifdef TASK_DYLD_INFO + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + Error err; + // The actual task shouldn't matter for the DYLD info, so lets just use ours + kern_return_t kret = ::task_info (GetTaskPortForProcessID (err), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); + if (kret == KERN_SUCCESS) + { + // We now have the address of the all image infos structure + return dyld_info.all_image_info_addr; + } +#endif + return LLDB_INVALID_ADDRESS; +} + + + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h new file mode 100644 index 000000000000..228cb7c516b8 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h @@ -0,0 +1,138 @@ +//===-- MachTask.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachTask_h__ +#define __MachTask_h__ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +#include +#include +#include + +// Project includes +#include "MachException.h" +#include "MachVMMemory.h" + +class ProcessMacOSX; + +class MachTask +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + MachTask (ProcessMacOSX *process); + + virtual + ~MachTask (); + + void + Clear (); + + kern_return_t + Suspend (); + + kern_return_t + Resume (); + + int32_t + GetSuspendCount () const; + + size_t + ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error& error); + + size_t + WriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error& error); + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions, lldb_private::Error& error); + + lldb_private::Error + DeallocateMemory (lldb::addr_t addr); + + mach_port_t + ExceptionPort () const; + + bool + ExceptionPortIsValid () const; + + kern_return_t + SaveExceptionPortInfo (); + + kern_return_t + RestoreExceptionPortInfo (); + + kern_return_t + ShutDownExceptionThread (); + + bool + StartExceptionThread (lldb_private::Error &err); + + lldb::addr_t + GetDYLDAllImageInfosAddress (); + + kern_return_t + BasicInfo (struct task_basic_info *info) const; + + static kern_return_t + BasicInfo (task_t task, struct task_basic_info *info); + + bool + IsValid () const; + + static bool + IsValid (task_t task); + + static void * + ExceptionThread (void *arg); + + task_t + GetTaskPort () const + { + return m_task; + } + + task_t + GetTaskPortForProcessID (lldb_private::Error &err); + + static task_t + GetTaskPortForProcessID (lldb::pid_t pid, lldb_private::Error &err); + + ProcessMacOSX * + Process () + { + return m_process; + } + + const ProcessMacOSX * + Process () const + { + return m_process; + } + +protected: + ProcessMacOSX * m_process; // The mach process that owns this MachTask + task_t m_task; + MachVMMemory m_vm_memory; // Special mach memory reading class that will take care of watching for page and region boundaries + MachException::PortInfo m_exc_port_info; // Saved settings for all exception ports + lldb::thread_t m_exception_thread; // Thread ID for the exception thread in case we need it + mach_port_t m_exception_port; // Exception port on which we will receive child exceptions + + // Maybe sort this by address and use find? + typedef std::map allocation_collection; + allocation_collection m_allocations; + +private: + DISALLOW_COPY_AND_ASSIGN (MachTask); +}; + +#endif // __MachTask_h__ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h new file mode 100644 index 000000000000..3166c2802f38 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h @@ -0,0 +1,48 @@ +//===-- MachThreadContext.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MachThreadContext_h_ +#define liblldb_MachThreadContext_h_ + +#include + +#include "MachException.h" + +class ThreadMacOSX; + +class MachThreadContext +{ +public: + MachThreadContext (ThreadMacOSX &thread) : + m_thread (thread) + { + } + + virtual ~MachThreadContext() + { + } + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const = 0; + + virtual void InitializeInstance() = 0; + virtual void ThreadWillResume () = 0; + virtual bool ShouldStop () = 0; + virtual void RefreshStateAfterStop() = 0; + virtual bool NotifyException (MachException::Data& exc) { return false; } + virtual bool StepNotComplete () { return false; } + virtual size_t GetStackFrameData(lldb_private::StackFrame *frame, std::vector >& fp_pc_pairs) { return 0; } +// virtual const uint8_t * SoftwareBreakpointOpcode (size_t byte_size) = 0; + +protected: + ThreadMacOSX &m_thread; + +}; + +#endif // #ifndef liblldb_MachThreadContext_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp new file mode 100644 index 000000000000..4f6b17c81a2e --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp @@ -0,0 +1,1884 @@ +//===-- MachThreadContext_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 "MachThreadContext_arm.h" + +#include + +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" +#include "ThreadMacOSX.h" + +using namespace lldb_private; + +//#define DNB_ARCH_MACH_ARM_DEBUG_SW_STEP 1 + +static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; +static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + +// ARM constants used during decoding +#define REG_RD 0 +#define LDM_REGLIST 1 +#define PC_REG 15 +#define PC_REGLIST_BIT 0x8000 + +// ARM conditions +#define COND_EQ 0x0 +#define COND_NE 0x1 +#define COND_CS 0x2 +#define COND_HS 0x2 +#define COND_CC 0x3 +#define COND_LO 0x3 +#define COND_MI 0x4 +#define COND_PL 0x5 +#define COND_VS 0x6 +#define COND_VC 0x7 +#define COND_HI 0x8 +#define COND_LS 0x9 +#define COND_GE 0xA +#define COND_LT 0xB +#define COND_GT 0xC +#define COND_LE 0xD +#define COND_AL 0xE +#define COND_UNCOND 0xF + +#define MASK_CPSR_T (1u << 5) +#define MASK_CPSR_J (1u << 24) + +#define MNEMONIC_STRING_SIZE 32 +#define OPERAND_STRING_SIZE 128 + +using namespace lldb; +using namespace lldb_private; + +MachThreadContext_arm::MachThreadContext_arm(ThreadMacOSX &thread) : + MachThreadContext(thread), + m_hw_single_chained_step_addr(LLDB_INVALID_ADDRESS), + m_bvr0_reg (LLDB_INVALID_REGNUM), + m_bcr0_reg (LLDB_INVALID_REGNUM), + m_bvr0_save (0), + m_bcr0_save (0) +{ +} + +MachThreadContext_arm::~MachThreadContext_arm() +{ +} + +RegisterContext * +MachThreadContext_arm::CreateRegisterContext (StackFrame *frame) const +{ + return new RegisterContextMach_arm(m_thread, frame); +} + +// Instance init function +void +MachThreadContext_arm::InitializeInstance() +{ + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx != NULL); + const RegisterInfo * reg_info; + reg_info = reg_ctx->GetRegisterInfoByName ("bvr0"); + if (reg_info) + m_bvr0_reg = reg_info->reg; + + reg_info = reg_ctx->GetRegisterInfoByName ("bcr0"); + if (reg_info) + m_bcr0_reg = reg_info->reg; +} + + + +uint32_t +MachThreadContext_arm::GetCPUType() +{ + return CPU_TYPE_ARM; +} + +void +MachThreadContext_arm::ThreadWillResume() +{ + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread.GetState() == eStateStepping) + { + bool step_handled = false; + // This is the primary thread, let the arch do anything it needs + if (m_thread.GetRegisterContext()->NumSupportedHardwareBreakpoints() > 0) + { +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + bool half_step = m_hw_single_chained_step_addr != LLDB_INVALID_ADDRESS; +#endif + step_handled = EnableHardwareSingleStep(true) == KERN_SUCCESS; +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + if (!half_step) + step_handled = false; +#endif + } + +#if defined (ENABLE_ARM_SINGLE_STEP) + if (!step_handled) + { + SetSingleStepSoftwareBreakpoints(); + } +#endif + } +} + +bool +MachThreadContext_arm::ShouldStop () +{ + return true; +} + +void +MachThreadContext_arm::RefreshStateAfterStop () +{ + EnableHardwareSingleStep (false) == KERN_SUCCESS; +} + +#if defined (ENABLE_ARM_SINGLE_STEP) + +bool +MachThreadContext_arm::ShouldStop () +{ + return true; +} + +bool +MachThreadContext_arm::RefreshStateAfterStop () +{ + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + + bool success = true; + + m_state.InvalidateRegisterSet (GPRRegSet); + m_state.InvalidateRegisterSet (VFPRegSet); + m_state.InvalidateRegisterSet (EXCRegSet); + + // Are we stepping a single instruction? + if (ReadGPRRegisters(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread.GetState() == eStateStepping) + { +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + // Hardware single step must work if we are going to test software + // single step functionality + assert(success); + if (m_hw_single_chained_step_addr == LLDB_INVALID_ADDRESS && m_sw_single_step_next_pc != LLDB_INVALID_ADDRESS) + { + uint32_t sw_step_next_pc = m_sw_single_step_next_pc & 0xFFFFFFFEu; + bool sw_step_next_pc_is_thumb = (m_sw_single_step_next_pc & 1) != 0; + bool actual_next_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0; + if (m_state.gpr.r[15] != sw_step_next_pc) + { + LogError("curr pc = 0x%8.8x - calculated single step target PC was incorrect: 0x%8.8x != 0x%8.8x", m_state.gpr.r[15], sw_step_next_pc, m_state.gpr.r[15]); + exit(1); + } + if (actual_next_pc_is_thumb != sw_step_next_pc_is_thumb) + { + LogError("curr pc = 0x%8.8x - calculated single step calculated mode mismatch: sw single mode = %s != %s", + m_state.gpr.r[15], + actual_next_pc_is_thumb ? "Thumb" : "ARM", + sw_step_next_pc_is_thumb ? "Thumb" : "ARM"); + exit(1); + } + m_sw_single_step_next_pc = LLDB_INVALID_ADDRESS; + } +#else +#if defined (ENABLE_ARM_SINGLE_STEP) + // Are we software single stepping? + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id) || m_sw_single_step_itblock_break_count) + { + // Remove any software single stepping breakpoints that we have set + + // Do we have a normal software single step breakpoint? + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id)) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: removing software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_break_id); + success = m_thread.Process()->DisableBreakpoint(m_sw_single_step_break_id, true); + m_sw_single_step_break_id = LLDB_INVALID_BREAK_ID; + } + + // Do we have any Thumb IT breakpoints? + if (m_sw_single_step_itblock_break_count > 0) + { + // See if we hit one of our Thumb IT breakpoints? + DNBBreakpoint *step_bp = m_thread.Process()->Breakpoints().FindByAddress(m_state.gpr.r[15]); + + if (step_bp) + { + // We did hit our breakpoint, tell the breakpoint it was + // hit so that it can run its callback routine and fixup + // the PC. + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: IT software single step breakpoint hit (breakID=%u)", __FUNCTION__, step_bp->GetID()); + step_bp->BreakpointHit(m_thread.Process()->ProcessID(), m_thread.GetID()); + } + + // Remove all Thumb IT breakpoints + for (int i = 0; i < m_sw_single_step_itblock_break_count; i++) + { + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: removing IT software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_itblock_break_id[i]); + success = m_thread.Process()->DisableBreakpoint(m_sw_single_step_itblock_break_id[i], true); + m_sw_single_step_itblock_break_id[i] = LLDB_INVALID_BREAK_ID; + } + } + m_sw_single_step_itblock_break_count = 0; + + // Decode instructions up to the current PC to ensure the internal decoder state is valid for the IT block + // The decoder has to decode each instruction in the IT block even if it is not executed so that + // the fields are correctly updated + DecodeITBlockInstructions(m_state.gpr.r[15]); + } + + } + else +#endif + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; +#endif + } + else + { + // The MachThread will automatically restore the suspend count + // in ShouldStop (), so we don't need to do anything here if + // we weren't the primary thread the last time + } + } + return; +} + + + +bool +MachThreadContext_arm::StepNotComplete () +{ + if (m_hw_single_chained_step_addr != LLDB_INVALID_ADDRESS) + { + kern_return_t kret = KERN_INVALID_ARGUMENT; + kret = ReadGPRRegisters(false); + if (kret == KERN_SUCCESS) + { + if (m_state.gpr.r[15] == m_hw_single_chained_step_addr) + { + //ProcessMacOSXLog::LogIf(PD_LOG_STEP, "Need to step some more at 0x%8.8x", m_hw_single_chained_step_addr); + return true; + } + } + } + + m_hw_single_chained_step_addr = LLDB_INVALID_ADDRESS; + return false; +} + + +void +MachThreadContext_arm::DecodeITBlockInstructions(lldb::addr_t curr_pc) + +{ + uint16_t opcode16; + uint32_t opcode32; + lldb::addr_t next_pc_in_itblock; + lldb::addr_t pc_in_itblock = m_last_decode_pc; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc); + + // Decode IT block instruction from the instruction following the m_last_decoded_instruction at + // PC m_last_decode_pc upto and including the instruction at curr_pc + if (m_thread.Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2) + { + opcode32 = opcode16; + pc_in_itblock += 2; + // Check for 32 bit thumb opcode and read the upper 16 bits if needed + if (((opcode32 & 0xE000) == 0xE000) && opcode32 & 0x1800) + { + // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for + // a 32 bit Thumb opcode + // Read bits 31:16 of a 32 bit Thumb opcode + if (m_thread.Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2) + { + pc_in_itblock += 2; + // 32 bit thumb opcode + opcode32 = (opcode32 << 16) | opcode16; + } + else + { + LogError("%s: Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", __FUNCTION__, pc_in_itblock); + } + } + } + else + { + LogError("%s: Error reading 16-bit Thumb instruction at pc=0x%8.8x", __FUNCTION__, pc_in_itblock); + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: pc_in_itblock=0x%8.8x, curr_pc=0x%8.8x", __FUNCTION__, pc_in_itblock, curr_pc); + + next_pc_in_itblock = pc_in_itblock; + while (next_pc_in_itblock <= curr_pc) + { + arm_error_t decodeError; + + m_last_decode_pc = pc_in_itblock; + decodeError = DecodeInstructionUsingDisassembler(pc_in_itblock, m_state.gpr.__cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc_in_itblock); + + pc_in_itblock = next_pc_in_itblock; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: next_pc_in_itblock=0x%8.8x", __FUNCTION__, next_pc_in_itblock); + } +} + +#endif + +// Set the single step bit in the processor status register. +kern_return_t +MachThreadContext_arm::EnableHardwareSingleStep (bool enable) +{ + Error err; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_STEP); + + if (log) log->Printf("%s( enable = %d )", __FUNCTION__, enable); + + if (m_bvr0_reg == LLDB_INVALID_REGNUM || m_bcr0_reg == LLDB_INVALID_REGNUM) + return KERN_INVALID_ARGUMENT; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + uint32_t bvr = 0; + uint32_t bcr = 0; + + const uint32_t i = 0; + if (enable) + { + m_hw_single_chained_step_addr = LLDB_INVALID_ADDRESS; + + // Save our previous state + m_bvr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bvr0_reg, 0); + m_bcr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bcr0_reg, 0); + lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); + lldb::addr_t cpsr = reg_ctx->GetFlags(0); + if (pc == LLDB_INVALID_ADDRESS) + return KERN_INVALID_ARGUMENT; + + // Set a breakpoint that will stop when the PC doesn't match the current one! + bvr = pc & 0xFFFFFFFCu; // Set the current PC as the breakpoint address + bcr = BCR_M_IMVA_MISMATCH | // Stop on address mismatch + S_USER | // Stop only in user mode + BCR_ENABLE; // Enable this breakpoint + if (cpsr & 0x20) + { + // Thumb breakpoint + if (pc & 2) + bcr |= BAS_IMVA_2_3; + else + bcr |= BAS_IMVA_0_1; + + uint16_t opcode; + Error error; + if (sizeof(opcode) == m_thread.GetProcess().ReadMemory(pc, &opcode, sizeof(opcode), error)) + { + if (((opcode & 0xE000) == 0xE000) && opcode & 0x1800) + { + // 32 bit thumb opcode... + if (pc & 2) + { + // We can't take care of a 32 bit thumb instruction single step + // with just IVA mismatching. We will need to chain an extra + // hardware single step in order to complete this single step... + m_hw_single_chained_step_addr = pc + 2; + } + else + { + // Extend the number of bits to ignore for the mismatch + bcr |= BAS_IMVA_ALL; + } + } + } + } + else + { + // ARM breakpoint + bcr |= BAS_IMVA_ALL; // Stop when any address bits change + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__, i, bvr, i, bcr); + + m_bvr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bvr0_reg, 0); + m_bcr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bcr0_reg, 0); + +// for (uint32_t j=i+1; j<16; ++j) +// { +// // Disable all others +// m_state.dbg.bvr[j] = 0; +// m_state.dbg.bcr[j] = 0; +// } + } + else + { + // Just restore the state we had before we did single stepping + bvr = m_bvr0_save; + bcr = m_bcr0_save; + } + + if (reg_ctx->WriteRegisterFromUnsigned(m_bvr0_reg, bvr) && + reg_ctx->WriteRegisterFromUnsigned(m_bcr0_reg, bcr)) + return KERN_SUCCESS; + + return KERN_INVALID_ARGUMENT; +} + +#if defined (ENABLE_ARM_SINGLE_STEP) + +// return 1 if bit "BIT" is set in "value" +static inline uint32_t bit(uint32_t value, uint32_t bit) +{ + return (value >> bit) & 1u; +} + +// return the bitfield "value[msbit:lsbit]". +static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit) +{ + assert(msbit >= lsbit); + uint32_t shift_left = sizeof(value) * 8 - 1 - msbit; + value <<= shift_left; // shift anything above the msbit off of the unsigned edge + value >>= shift_left + lsbit; // shift it back again down to the lsbit (including undoing any shift from above) + return value; // return our result +} + +bool +MachThreadContext_arm::ConditionPassed(uint8_t condition, uint32_t cpsr) +{ + uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag + uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag + uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag + uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag + + switch (condition) { + case COND_EQ: // (0x0) + if (cpsr_z == 1) return true; + break; + case COND_NE: // (0x1) + if (cpsr_z == 0) return true; + break; + case COND_CS: // (0x2) + if (cpsr_c == 1) return true; + break; + case COND_CC: // (0x3) + if (cpsr_c == 0) return true; + break; + case COND_MI: // (0x4) + if (cpsr_n == 1) return true; + break; + case COND_PL: // (0x5) + if (cpsr_n == 0) return true; + break; + case COND_VS: // (0x6) + if (cpsr_v == 1) return true; + break; + case COND_VC: // (0x7) + if (cpsr_v == 0) return true; + break; + case COND_HI: // (0x8) + if ((cpsr_c == 1) && (cpsr_z == 0)) return true; + break; + case COND_LS: // (0x9) + if ((cpsr_c == 0) || (cpsr_z == 1)) return true; + break; + case COND_GE: // (0xA) + if (cpsr_n == cpsr_v) return true; + break; + case COND_LT: // (0xB) + if (cpsr_n != cpsr_v) return true; + break; + case COND_GT: // (0xC) + if ((cpsr_z == 0) && (cpsr_n == cpsr_v)) return true; + break; + case COND_LE: // (0xD) + if ((cpsr_z == 1) || (cpsr_n != cpsr_v)) return true; + break; + default: + return true; + break; + } + + return false; +} + +bool +MachThreadContext_arm::ComputeNextPC(lldb::addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, lldb::addr_t *targetPC) +{ + lldb::addr_t myTargetPC, addressWherePCLives; + lldb::pid_t mypid; + + uint32_t cpsr_c = bit(m_state.gpr.cpsr, 29); // Carry condition code flag + + uint32_t firstOperand=0, secondOperand=0, shiftAmount=0, secondOperandAfterShift=0, immediateValue=0; + uint32_t halfwords=0, baseAddress=0, immediateOffset=0, addressOffsetFromRegister=0, addressOffsetFromRegisterAfterShift; + uint32_t baseAddressIndex=LLDB_INVALID_INDEX32; + uint32_t firstOperandIndex=LLDB_INVALID_INDEX32; + uint32_t secondOperandIndex=LLDB_INVALID_INDEX32; + uint32_t addressOffsetFromRegisterIndex=LLDB_INVALID_INDEX32; + uint32_t shiftRegisterIndex=LLDB_INVALID_INDEX32; + uint16_t registerList16, registerList16NoPC; + uint8_t registerList8; + uint32_t numRegistersToLoad=0; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: instruction->code=%d", __FUNCTION__, decodedInstruction.instruction->code); + + // Get the following in this switch statement: + // - firstOperand, secondOperand, immediateValue, shiftAmount: For arithmetic, logical and move instructions + // - baseAddress, immediateOffset, shiftAmount: For LDR + // - numRegistersToLoad: For LDM and POP instructions + switch (decodedInstruction.instruction->code) + { + // Arithmetic operations that can change the PC + case ARM_INST_ADC: + case ARM_INST_ADCS: + case ARM_INST_ADD: + case ARM_INST_ADDS: + case ARM_INST_AND: + case ARM_INST_ANDS: + case ARM_INST_ASR: + case ARM_INST_ASRS: + case ARM_INST_BIC: + case ARM_INST_BICS: + case ARM_INST_EOR: + case ARM_INST_EORS: + case ARM_INST_ORR: + case ARM_INST_ORRS: + case ARM_INST_RSB: + case ARM_INST_RSBS: + case ARM_INST_RSC: + case ARM_INST_RSCS: + case ARM_INST_SBC: + case ARM_INST_SBCS: + case ARM_INST_SUB: + case ARM_INST_SUBS: + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_DATA_IMM: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get immediateValue (at index=2) + immediateValue = decodedInstruction.op[2].value; + + break; + + case ARM_ADDR_DATA_REG: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + case ARM_ADDR_DATA_SCALED_IMM: + if (decodedInstruction.numOperands != 4) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount as immediate value (at index=3) + shiftAmount = decodedInstruction.op[3].value; + + break; + + + case ARM_ADDR_DATA_SCALED_REG: + if (decodedInstruction.numOperands != 4) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount from register (at index=3) + shiftRegisterIndex = decodedInstruction.op[3].value; // second operand register index + shiftAmount = m_state.gpr.r[shiftRegisterIndex]; + + break; + + case THUMB_ADDR_HR_HR: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=0) + firstOperandIndex = decodedInstruction.op[0].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + default: + break; + } + break; + + // Logical shifts and move operations that can change the PC + case ARM_INST_LSL: + case ARM_INST_LSLS: + case ARM_INST_LSR: + case ARM_INST_LSRS: + case ARM_INST_MOV: + case ARM_INST_MOVS: + case ARM_INST_MVN: + case ARM_INST_MVNS: + case ARM_INST_ROR: + case ARM_INST_RORS: + case ARM_INST_RRX: + case ARM_INST_RRXS: + // In these cases, the firstOperand is always 0, as if it does not exist + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_DATA_IMM: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get immediateValue (at index=1) + immediateValue = decodedInstruction.op[1].value; + + break; + + case ARM_ADDR_DATA_REG: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + case ARM_ADDR_DATA_SCALED_IMM: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount as immediate value (at index=2) + shiftAmount = decodedInstruction.op[2].value; + + break; + + + case ARM_ADDR_DATA_SCALED_REG: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount from register (at index=2) + shiftRegisterIndex = decodedInstruction.op[2].value; // second operand register index + shiftAmount = m_state.gpr.r[shiftRegisterIndex]; + + break; + + case THUMB_ADDR_HR_HR: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + default: + break; + } + + break; + + // Simple branches, used to hop around within a routine + case ARM_INST_B: + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + break; + + // Branch-and-link, used to call ARM subroutines + case ARM_INST_BL: + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + break; + + // Branch-and-link with exchange, used to call opposite-mode subroutines + case ARM_INST_BLX: + if ((decodedInstruction.addressMode == ARM_ADDR_BRANCH_IMM) || + (decodedInstruction.addressMode == THUMB_ADDR_UNCOND)) + { + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + else // addressMode == ARM_ADDR_BRANCH_REG + { + // Unknown target unless we're branching to the PC itself, + // although this may not work properly with BLX + if (decodedInstruction.op[REG_RD].value == PC_REG) + { + // this should (almost) never happen + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + + // Get the branch address and return + if (decodedInstruction.numOperands != 1) + { + LogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address in register (at index=0) + *targetPC = m_state.gpr.r[decodedInstruction.op[0].value]; + return true; + } + break; + + // Branch with exchange, used to hop to opposite-mode code + // Branch to Jazelle code, used to execute Java; included here since it + // acts just like BX unless the Jazelle unit is active and JPC is + // already loaded into it. + case ARM_INST_BX: + case ARM_INST_BXJ: + // Unknown target unless we're branching to the PC itself, + // although this can never switch to Thumb mode and is + // therefore pretty much useless + if (decodedInstruction.op[REG_RD].value == PC_REG) + { + // this should (almost) never happen + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + + // Get the branch address and return + if (decodedInstruction.numOperands != 1) + { + LogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address in register (at index=0) + *targetPC = m_state.gpr.r[decodedInstruction.op[0].value]; + return true; + break; + + // Compare and branch on zero/non-zero (Thumb-16 only) + // Unusual condition check built into the instruction + case ARM_INST_CBZ: + case ARM_INST_CBNZ: + // Branch address is known at compile time + // Get the branch address and return + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address as an immediate value (at index=1) + *targetPC = decodedInstruction.op[1].value; + return true; + break; + + // Load register can be used to load PC, usually with a function pointer + case ARM_INST_LDR: + if (decodedInstruction.op[REG_RD].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_LSWUB_IMM: + case ARM_ADDR_LSWUB_IMM_PRE: + case ARM_ADDR_LSWUB_IMM_POST: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset (at index=2) + immediateOffset = decodedInstruction.op[2].value; + break; + + case ARM_ADDR_LSWUB_REG: + case ARM_ADDR_LSWUB_REG_PRE: + case ARM_ADDR_LSWUB_REG_POST: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset from register (at index=2) + addressOffsetFromRegisterIndex = decodedInstruction.op[2].value; + addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex]; + + break; + + case ARM_ADDR_LSWUB_SCALED: + case ARM_ADDR_LSWUB_SCALED_PRE: + case ARM_ADDR_LSWUB_SCALED_POST: + if (decodedInstruction.numOperands != 4) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset from register (at index=2) + addressOffsetFromRegisterIndex = decodedInstruction.op[2].value; + addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex]; + + // Get shiftAmount (at index=3) + shiftAmount = decodedInstruction.op[3].value; + + break; + + default: + break; + } + break; + + // 32b load multiple operations can load the PC along with everything else, + // usually to return from a function call + case ARM_INST_LDMDA: + case ARM_INST_LDMDB: + case ARM_INST_LDMIA: + case ARM_INST_LDMIB: + if (decodedInstruction.op[LDM_REGLIST].value & PC_REGLIST_BIT) + { + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=0) + baseAddressIndex = decodedInstruction.op[0].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get registerList from register (at index=1) + registerList16 = (uint16_t)decodedInstruction.op[1].value; + + // Count number of registers to load in the multiple register list excluding the PC + registerList16NoPC = registerList16&0x3FFF; // exclude the PC + numRegistersToLoad=0; + for (int i = 0; i < 16; i++) + { + if (registerList16NoPC & 0x1) numRegistersToLoad++; + registerList16NoPC = registerList16NoPC >> 1; + } + } + else + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + break; + + // Normal 16-bit LD multiple can't touch R15, but POP can + case ARM_INST_POP: // Can also get the PC & updates SP + // Get baseAddress from SP (at index=0) + baseAddress = m_state.gpr.__sp; + + if (decodedInstruction.thumb16b) + { + // Get registerList from register (at index=0) + registerList8 = (uint8_t)decodedInstruction.op[0].value; + + // Count number of registers to load in the multiple register list + numRegistersToLoad=0; + for (int i = 0; i < 8; i++) + { + if (registerList8 & 0x1) numRegistersToLoad++; + registerList8 = registerList8 >> 1; + } + } + else + { + // Get registerList from register (at index=0) + registerList16 = (uint16_t)decodedInstruction.op[0].value; + + // Count number of registers to load in the multiple register list excluding the PC + registerList16NoPC = registerList16&0x3FFF; // exclude the PC + numRegistersToLoad=0; + for (int i = 0; i < 16; i++) + { + if (registerList16NoPC & 0x1) numRegistersToLoad++; + registerList16NoPC = registerList16NoPC >> 1; + } + } + break; + + // 16b TBB and TBH instructions load a jump address from a table + case ARM_INST_TBB: + case ARM_INST_TBH: + // Get baseAddress from register (at index=0) + baseAddressIndex = decodedInstruction.op[0].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset from register (at index=1) + addressOffsetFromRegisterIndex = decodedInstruction.op[1].value; + addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex]; + break; + + // ThumbEE branch-to-handler instructions: Jump to handlers at some offset + // from a special base pointer register (which is unknown at disassembly time) + case ARM_INST_HB: + case ARM_INST_HBP: +// TODO: ARM_INST_HB, ARM_INST_HBP + break; + + case ARM_INST_HBL: + case ARM_INST_HBLP: +// TODO: ARM_INST_HBL, ARM_INST_HBLP + break; + + // Breakpoint and software interrupt jump to interrupt handler (always ARM) + case ARM_INST_BKPT: + case ARM_INST_SMC: + case ARM_INST_SVC: + + // Return from exception, obviously modifies PC [interrupt only!] + case ARM_INST_RFEDA: + case ARM_INST_RFEDB: + case ARM_INST_RFEIA: + case ARM_INST_RFEIB: + + // Other instructions either can't change R15 or are "undefined" if you do, + // so no sane compiler should ever generate them & we don't care here. + // Also, R15 can only legally be used in a read-only manner for the + // various ARM addressing mode (to get PC-relative addressing of constants), + // but can NOT be used with any of the update modes. + default: + LogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code); + return false; + break; + } + + // Adjust PC if PC is one of the input operands + if (baseAddressIndex == PC_REG) + { + if (currentPCIsThumb) + baseAddress += 4; + else + baseAddress += 8; + } + + if (firstOperandIndex == PC_REG) + { + if (currentPCIsThumb) + firstOperand += 4; + else + firstOperand += 8; + } + + if (secondOperandIndex == PC_REG) + { + if (currentPCIsThumb) + secondOperand += 4; + else + secondOperand += 8; + } + + if (addressOffsetFromRegisterIndex == PC_REG) + { + if (currentPCIsThumb) + addressOffsetFromRegister += 4; + else + addressOffsetFromRegister += 8; + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, + "%s: firstOperand=%8.8x, secondOperand=%8.8x, immediateValue = %d, shiftAmount = %d, baseAddress = %8.8x, addressOffsetFromRegister = %8.8x, immediateOffset = %d, numRegistersToLoad = %d", + __FUNCTION__, + firstOperand, + secondOperand, + immediateValue, + shiftAmount, + baseAddress, + addressOffsetFromRegister, + immediateOffset, + numRegistersToLoad); + + + // Calculate following values after applying shiftAmount: + // - immediateOffsetAfterShift, secondOperandAfterShift + + switch (decodedInstruction.scaleMode) + { + case ARM_SCALE_NONE: + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister; + secondOperandAfterShift = secondOperand; + break; + + case ARM_SCALE_LSL: // Logical shift left + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister << shiftAmount; + secondOperandAfterShift = secondOperand << shiftAmount; + break; + + case ARM_SCALE_LSR: // Logical shift right + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister >> shiftAmount; + secondOperandAfterShift = secondOperand >> shiftAmount; + break; + + case ARM_SCALE_ASR: // Arithmetic shift right + asm("mov %0, %1, asr %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount)); + asm("mov %0, %1, asr %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount)); + break; + + case ARM_SCALE_ROR: // Rotate right + asm("mov %0, %1, ror %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount)); + asm("mov %0, %1, ror %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount)); + break; + + case ARM_SCALE_RRX: // Rotate right, pulling in carry (1-bit shift only) + asm("mov %0, %1, rrx" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister)); + asm("mov %0, %1, rrx" : "=r" (secondOperandAfterShift) : "r" (secondOperand)); + break; + } + + // Emulate instruction to calculate targetPC + // All branches are already handled in the first switch statement. A branch should not reach this switch + switch (decodedInstruction.instruction->code) + { + // Arithmetic operations that can change the PC + case ARM_INST_ADC: + case ARM_INST_ADCS: + // Add with Carry + *targetPC = firstOperand + (secondOperandAfterShift + immediateValue) + cpsr_c; + break; + + case ARM_INST_ADD: + case ARM_INST_ADDS: + *targetPC = firstOperand + (secondOperandAfterShift + immediateValue); + break; + + case ARM_INST_AND: + case ARM_INST_ANDS: + *targetPC = firstOperand & (secondOperandAfterShift + immediateValue); + break; + + case ARM_INST_ASR: + case ARM_INST_ASRS: + asm("mov %0, %1, asr %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_BIC: + case ARM_INST_BICS: + asm("bic %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_EOR: + case ARM_INST_EORS: + asm("eor %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_ORR: + case ARM_INST_ORRS: + asm("orr %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_RSB: + case ARM_INST_RSBS: + asm("rsb %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_RSC: + case ARM_INST_RSCS: + myTargetPC = secondOperandAfterShift - (firstOperand + !cpsr_c); + *targetPC = myTargetPC; + break; + + case ARM_INST_SBC: + case ARM_INST_SBCS: + asm("sbc %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue + !cpsr_c)); + *targetPC = myTargetPC; + break; + + case ARM_INST_SUB: + case ARM_INST_SUBS: + asm("sub %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + // Logical shifts and move operations that can change the PC + case ARM_INST_LSL: + case ARM_INST_LSLS: + case ARM_INST_LSR: + case ARM_INST_LSRS: + case ARM_INST_MOV: + case ARM_INST_MOVS: + case ARM_INST_ROR: + case ARM_INST_RORS: + case ARM_INST_RRX: + case ARM_INST_RRXS: + myTargetPC = secondOperandAfterShift + immediateValue; + *targetPC = myTargetPC; + break; + + case ARM_INST_MVN: + case ARM_INST_MVNS: + myTargetPC = !(secondOperandAfterShift + immediateValue); + *targetPC = myTargetPC; + break; + + // Load register can be used to load PC, usually with a function pointer + case ARM_INST_LDR: + switch (decodedInstruction.addressMode) { + case ARM_ADDR_LSWUB_IMM_POST: + case ARM_ADDR_LSWUB_REG_POST: + case ARM_ADDR_LSWUB_SCALED_POST: + addressWherePCLives = baseAddress; + break; + + case ARM_ADDR_LSWUB_IMM: + case ARM_ADDR_LSWUB_REG: + case ARM_ADDR_LSWUB_SCALED: + case ARM_ADDR_LSWUB_IMM_PRE: + case ARM_ADDR_LSWUB_REG_PRE: + case ARM_ADDR_LSWUB_SCALED_PRE: + addressWherePCLives = baseAddress + (addressOffsetFromRegisterAfterShift + immediateOffset); + break; + + default: + break; + } + + mypid = m_thread.ProcessID(); + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + // 32b load multiple operations can load the PC along with everything else, + // usually to return from a function call + case ARM_INST_LDMDA: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMDB: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress - 4; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMIB: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress + numRegistersToLoad*4 + 4; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMIA: // same as pop + // Normal 16-bit LD multiple can't touch R15, but POP can + case ARM_INST_POP: // Can also get the PC & updates SP + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress + numRegistersToLoad*4; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + // 16b TBB and TBH instructions load a jump address from a table + case ARM_INST_TBB: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress + addressOffsetFromRegisterAfterShift; + if (PDProcessMemoryRead(mypid, addressWherePCLives, 1, &halfwords) != 1) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the TBB instruction!", addressWherePCLives); + return false; + } + // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords + *targetPC = (currentPC + 4) + 2*halfwords; + break; + + case ARM_INST_TBH: + mypid = m_thread.ProcessID(); + addressWherePCLives = ((baseAddress + (addressOffsetFromRegisterAfterShift << 1)) & ~0x1); + if (PDProcessMemoryRead(mypid, addressWherePCLives, 2, &halfwords) != 2) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the TBH instruction!", addressWherePCLives); + return false; + } + // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords + *targetPC = (currentPC + 4) + 2*halfwords; + break; + + // ThumbEE branch-to-handler instructions: Jump to handlers at some offset + // from a special base pointer register (which is unknown at disassembly time) + case ARM_INST_HB: + case ARM_INST_HBP: + // TODO: ARM_INST_HB, ARM_INST_HBP + break; + + case ARM_INST_HBL: + case ARM_INST_HBLP: + // TODO: ARM_INST_HBL, ARM_INST_HBLP + break; + + // Breakpoint and software interrupt jump to interrupt handler (always ARM) + case ARM_INST_BKPT: + case ARM_INST_SMC: + case ARM_INST_SVC: + // TODO: ARM_INST_BKPT, ARM_INST_SMC, ARM_INST_SVC + break; + + // Return from exception, obviously modifies PC [interrupt only!] + case ARM_INST_RFEDA: + case ARM_INST_RFEDB: + case ARM_INST_RFEIA: + case ARM_INST_RFEIB: + // TODO: ARM_INST_RFEDA, ARM_INST_RFEDB, ARM_INST_RFEIA, ARM_INST_RFEIB + break; + + // Other instructions either can't change R15 or are "undefined" if you do, + // so no sane compiler should ever generate them & we don't care here. + // Also, R15 can only legally be used in a read-only manner for the + // various ARM addressing mode (to get PC-relative addressing of constants), + // but can NOT be used with any of the update modes. + default: + LogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code); + return false; + break; + } + + return true; +} + +void +MachThreadContext_arm::EvaluateNextInstructionForSoftwareBreakpointSetup(lldb::addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, lldb::addr_t *nextPC, bool *nextPCIsThumb) +{ + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "MachThreadContext_arm::EvaluateNextInstructionForSoftwareBreakpointSetup() called"); + + lldb::addr_t targetPC = LLDB_INVALID_ADDRESS; + uint32_t registerValue; + arm_error_t decodeError; + lldb::addr_t currentPCInITBlock, nextPCInITBlock; + int i; + bool last_decoded_instruction_executes = true; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: default nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM"); + + // Update *nextPC and *nextPCIsThumb for special cases + if (m_last_decode_thumb.itBlockRemaining) // we are in an IT block + { + // Set the nextPC to the PC of the instruction which will execute in the IT block + // If none of the instruction execute in the IT block based on the condition flags, + // then point to the instruction immediately following the IT block + const int itBlockRemaining = m_last_decode_thumb.itBlockRemaining; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: itBlockRemaining=%8.8x", __FUNCTION__, itBlockRemaining); + + // Determine the PC at which the next instruction resides + if (m_last_decode_arm.thumb16b) + currentPCInITBlock = currentPC + 2; + else + currentPCInITBlock = currentPC + 4; + + for (i = 0; i < itBlockRemaining; i++) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: currentPCInITBlock=%8.8x", __FUNCTION__, currentPCInITBlock); + decodeError = DecodeInstructionUsingDisassembler(currentPCInITBlock, cpsr, &m_last_decode_arm, &m_last_decode_thumb, &nextPCInITBlock); + + if (decodeError != ARM_SUCCESS) + LogError("unable to disassemble instruction at 0x%8.8lx", currentPCInITBlock); + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: condition=%d", __FUNCTION__, m_last_decode_arm.condition); + if (ConditionPassed(m_last_decode_arm.condition, cpsr)) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition codes matched for instruction %d", __FUNCTION__, i); + break; // break from the for loop + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition codes DID NOT matched for instruction %d", __FUNCTION__, i); + } + + // update currentPC and nextPCInITBlock + currentPCInITBlock = nextPCInITBlock; + } + + if (i == itBlockRemaining) // We came out of the IT block without executing any instructions + last_decoded_instruction_executes = false; + + *nextPC = currentPCInITBlock; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: After IT block step-through: *nextPC=%8.8x", __FUNCTION__, *nextPC); + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, + "%s: cpsr = %8.8x, thumb16b = %d, thumb = %d, branch = %d, conditional = %d, knownTarget = %d, links = %d, canSwitchMode = %d, doesSwitchMode = %d", + __FUNCTION__, + cpsr, + m_last_decode_arm.thumb16b, + m_last_decode_arm.thumb, + m_last_decode_arm.branch, + m_last_decode_arm.conditional, + m_last_decode_arm.knownTarget, + m_last_decode_arm.links, + m_last_decode_arm.canSwitchMode, + m_last_decode_arm.doesSwitchMode); + + + if (last_decoded_instruction_executes && // Was this a conditional instruction that did execute? + m_last_decode_arm.branch && // Can this instruction change the PC? + (m_last_decode_arm.instruction->code != ARM_INST_SVC)) // If this instruction is not an SVC instruction + { + // Set targetPC. Compute if needed. + if (m_last_decode_arm.knownTarget) + { + // Fixed, known PC-relative + targetPC = m_last_decode_arm.targetPC; + } + else + { + // if targetPC is not known at compile time (PC-relative target), compute targetPC + if (!ComputeNextPC(currentPC, m_last_decode_arm, currentPCIsThumb, &targetPC)) + { + LogError("%s: Unable to compute targetPC for instruction at 0x%8.8lx", __FUNCTION__, currentPC); + targetPC = LLDB_INVALID_ADDRESS; + } + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: targetPC=0x%8.8x, cpsr=0x%8.8x, condition=0x%hhx", __FUNCTION__, targetPC, cpsr, m_last_decode_arm.condition); + + // Refine nextPC computation + if ((m_last_decode_arm.instruction->code == ARM_INST_CBZ) || + (m_last_decode_arm.instruction->code == ARM_INST_CBNZ)) + { + // Compare and branch on zero/non-zero (Thumb-16 only) + // Unusual condition check built into the instruction + registerValue = m_state.gpr.r[m_last_decode_arm.op[REG_RD].value]; + + if (m_last_decode_arm.instruction->code == ARM_INST_CBZ) + { + if (registerValue == 0) + *nextPC = targetPC; + } + else + { + if (registerValue != 0) + *nextPC = targetPC; + } + } + else if (m_last_decode_arm.conditional) // Is the change conditional on flag results? + { + if (ConditionPassed(m_last_decode_arm.condition, cpsr)) // conditions match + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition matched!", __FUNCTION__); + *nextPC = targetPC; + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition did not match!", __FUNCTION__); + } + } + else + { + *nextPC = targetPC; + } + + // Refine nextPCIsThumb computation + if (m_last_decode_arm.doesSwitchMode) + { + *nextPCIsThumb = !currentPCIsThumb; + } + else if (m_last_decode_arm.canSwitchMode) + { + // Legal to switch ARM <--> Thumb mode with this branch + // dependent on bit[0] of targetPC + *nextPCIsThumb = (*nextPC & 1u) != 0; + } + else + { + *nextPCIsThumb = currentPCIsThumb; + } + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: calculated nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM"); +} + + +arm_error_t +MachThreadContext_arm::DecodeInstructionUsingDisassembler(lldb::addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, lldb::addr_t *next_pc) +{ + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: pc=0x%8.8x, cpsr=0x%8.8x", __FUNCTION__, curr_pc, curr_cpsr); + + const uint32_t isetstate_mask = MASK_CPSR_T | MASK_CPSR_J; + const uint32_t curr_isetstate = curr_cpsr & isetstate_mask; + uint32_t opcode32; + lldb::addr_t nextPC = curr_pc; + arm_error_t decodeReturnCode = ARM_SUCCESS; + + m_last_decode_pc = curr_pc; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc); + + switch (curr_isetstate) { + case 0x0: // ARM Instruction + // Read the ARM opcode + if (m_thread.Process()->Task().ReadMemory(curr_pc, 4, &opcode32) != 4) + { + LogError("unable to read opcode bits 31:0 for an ARM opcode at 0x%8.8lx", curr_pc); + decodeReturnCode = ARM_ERROR; + } + else + { + nextPC += 4; + decodeReturnCode = ArmDisassembler((uint64_t)curr_pc, opcode32, false, decodedInstruction, NULL, 0, NULL, 0); + + if (decodeReturnCode != ARM_SUCCESS) + LogError("Unable to decode ARM instruction 0x%8.8x at 0x%8.8lx", opcode32, curr_pc); + } + break; + + case 0x20: // Thumb Instruction + uint16_t opcode16; + // Read the a 16 bit Thumb opcode + if (m_thread.Process()->Task().ReadMemory(curr_pc, 2, &opcode16) != 2) + { + LogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc); + decodeReturnCode = ARM_ERROR; + } + else + { + nextPC += 2; + opcode32 = opcode16; + + decodeReturnCode = ThumbDisassembler((uint64_t)curr_pc, opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0); + + switch (decodeReturnCode) { + case ARM_SKIP: + // 32 bit thumb opcode + nextPC += 2; + if (m_thread.Process()->Task().ReadMemory(curr_pc+2, 2, &opcode16) != 2) + { + LogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc+2); + } + else + { + opcode32 = (opcode32 << 16) | opcode16; + + decodeReturnCode = ThumbDisassembler((uint64_t)(curr_pc+2), opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0); + + if (decodeReturnCode != ARM_SUCCESS) + LogError("Unable to decode 2nd half of Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc+2); + break; + } + break; + + case ARM_SUCCESS: + // 16 bit thumb opcode; at this point we are done decoding the opcode + break; + + default: + LogError("Unable to decode Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc); + decodeReturnCode = ARM_ERROR; + break; + } + } + break; + + default: + break; + } + + if (next_pc) + *next_pc = nextPC; + + return decodeReturnCode; +} + +bool +MachThreadContext_arm::BreakpointHit(lldb::pid_t pid, lldb::tid_t tid, lldb::user_id_t breakID, void *baton) +{ + lldb::addr_t bkpt_pc = (lldb::addr_t)baton; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s(pid = %i, tid = %4.4x, breakID = %u, baton = %p): Setting PC to 0x%8.8x", __FUNCTION__, pid, tid, breakID, baton, bkpt_pc); + return PDThreadSetRegisterValueByID(pid, tid, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, bkpt_pc); +} + +// Set the single step bit in the processor status register. +kern_return_t +MachThreadContext_arm::SetSingleStepSoftwareBreakpoints() +{ + Error err; + err = ReadGPRRegisters(false); + + if (err.Fail()) + { + err.Log("%s: failed to read the GPR registers", __FUNCTION__); + return err.GetError(); + } + + lldb::addr_t curr_pc = m_state.gpr.r[15]; + uint32_t curr_cpsr = m_state.gpr.__cpsr; + lldb::addr_t next_pc = curr_pc; + + bool curr_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0; + bool next_pc_is_thumb = curr_pc_is_thumb; + + uint32_t curr_itstate = ((curr_cpsr & 0x6000000) >> 25) | ((curr_cpsr & 0xFC00) >> 8); + bool inITBlock = (curr_itstate & 0xF) ? 1 : 0; + bool lastInITBlock = ((curr_itstate & 0xF) == 0x8) ? 1 : 0; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: curr_pc=0x%8.8x (%s), curr_itstate=0x%x, inITBlock=%d, lastInITBlock=%d", __FUNCTION__, curr_pc, curr_pc_is_thumb ? "Thumb" : "ARM", curr_itstate, inITBlock, lastInITBlock); + + // If the instruction is not in the IT block, then decode using the Disassembler and compute next_pc + if (!inITBlock) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Decoding an instruction NOT in the IT block", __FUNCTION__); + + arm_error_t decodeReturnCode = DecodeInstructionUsingDisassembler(curr_pc, curr_cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc); + + if (decodeReturnCode != ARM_SUCCESS) + { + err = KERN_INVALID_ARGUMENT; + LogError("MachThreadContext_arm::SetSingleStepSoftwareBreakpoints: Unable to disassemble instruction at 0x%8.8lx", curr_pc); + } + } + else + { + next_pc = curr_pc + ((m_last_decode_arm.thumb16b) ? 2 : 4); + } + + // Instruction is NOT in the IT block OR + if (!inITBlock) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: normal instruction", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else if (inITBlock && !m_last_decode_arm.setsFlags) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction that doesn't set flags", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else if (lastInITBlock && m_last_decode_arm.branch) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction which last in the IT block and is a branch", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else + { + // Instruction is in IT block and can modify the CPSR flags + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction that sets flags", __FUNCTION__); + + // NOTE: When this point of code is reached, the instruction at curr_pc has already been decoded + // inside the function ShouldStop (). Therefore m_last_decode_arm, m_last_decode_thumb + // reflect the decoded instruction at curr_pc + + // If we find an instruction inside the IT block which will set/modify the condition flags (NZCV bits in CPSR), + // we set breakpoints at all remaining instructions inside the IT block starting from the instruction immediately + // following this one AND a breakpoint at the instruction immediately following the IT block. We do this because + // we cannot determine the next_pc until the instruction at which we are currently stopped executes. Hence we + // insert (m_last_decode_thumb.itBlockRemaining+1) 16-bit Thumb breakpoints at consecutive memory locations + // starting at addrOfNextInstructionInITBlock. We record these breakpoints in class variable m_sw_single_step_itblock_break_id[], + // and also record the total number of IT breakpoints set in the variable 'm_sw_single_step_itblock_break_count'. + + // The instructions inside the IT block, which are replaced by the 16-bit Thumb breakpoints (opcode=0xDEFE) + // instructions, can be either Thumb-16 or Thumb-32. When a Thumb-32 instruction (say, inst#1) is replaced Thumb + // by a 16-bit breakpoint (OS only supports 16-bit breakpoints in Thumb mode and 32-bit breakpoints in ARM mode), the + // breakpoint for the next instruction (say instr#2) is saved in the upper half of this Thumb-32 (instr#1) + // instruction. Hence if the execution stops at Breakpoint2 corresponding to instr#2, the PC is offset by 16-bits. + // We therefore have to keep track of PC of each instruction in the IT block that is being replaced with the 16-bit + // Thumb breakpoint, to ensure that when the breakpoint is hit, the PC is adjusted to the correct value. We save + // the actual PC corresponding to each instruction in the IT block by associating a call back with each breakpoint + // we set and passing it as a baton. When the breakpoint hits and the callback routine is called, the routine + // adjusts the PC based on the baton that is passed to it. + + lldb::addr_t addrOfNextInstructionInITBlock, pcInITBlock, nextPCInITBlock, bpAddressInITBlock; + uint16_t opcode16; + uint32_t opcode32; + + addrOfNextInstructionInITBlock = (m_last_decode_arm.thumb16b) ? curr_pc + 2 : curr_pc + 4; + + pcInITBlock = addrOfNextInstructionInITBlock; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: itBlockRemaining=%d", __FUNCTION__, m_last_decode_thumb.itBlockRemaining); + + m_sw_single_step_itblock_break_count = 0; + for (int i = 0; i <= m_last_decode_thumb.itBlockRemaining; i++) + { + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + { + LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Array m_sw_single_step_itblock_break_id should not contain any valid breakpoint IDs at this point. But found a valid breakID=%d at index=%d", m_sw_single_step_itblock_break_id[i], i); + } + else + { + nextPCInITBlock = pcInITBlock; + // Compute nextPCInITBlock based on opcode present at pcInITBlock + if (m_thread.Process()->Task().ReadMemory(pcInITBlock, 2, &opcode16) == 2) + { + opcode32 = opcode16; + nextPCInITBlock += 2; + + // Check for 32 bit thumb opcode and read the upper 16 bits if needed + if (((opcode32 & 0xE000) == 0xE000) && (opcode32 & 0x1800)) + { + // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for + // a 32 bit Thumb opcode + // Read bits 31:16 of a 32 bit Thumb opcode + if (m_thread.Process()->Task().ReadMemory(pcInITBlock+2, 2, &opcode16) == 2) + { + // 32 bit thumb opcode + opcode32 = (opcode32 << 16) | opcode16; + nextPCInITBlock += 2; + } + else + { + LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", nextPCInITBlock); + } + } + } + else + { + LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Error reading 16-bit Thumb instruction at pc=0x%8.8x", nextPCInITBlock); + } + + + // Set breakpoint and associate a callback function with it + bpAddressInITBlock = addrOfNextInstructionInITBlock + 2*i; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Setting IT breakpoint[%d] at address: 0x%8.8x", __FUNCTION__, i, bpAddressInITBlock); + + m_sw_single_step_itblock_break_id[i] = m_thread.Process()->CreateBreakpoint(bpAddressInITBlock, 2, false, m_thread.GetID()); + if (!LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + err = KERN_INVALID_ARGUMENT; + else + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: Set IT breakpoint[%i]=%d set at 0x%8.8x for instruction at 0x%8.8x", __FUNCTION__, i, m_sw_single_step_itblock_break_id[i], bpAddressInITBlock, pcInITBlock); + + // Set the breakpoint callback for these special IT breakpoints + // so that if one of these breakpoints gets hit, it knows to + // update the PC to the original address of the conditional + // IT instruction. + PDBreakpointSetCallback(m_thread.ProcessID(), m_sw_single_step_itblock_break_id[i], MachThreadContext_arm::BreakpointHit, (void*)pcInITBlock); + m_sw_single_step_itblock_break_count++; + } + } + + pcInITBlock = nextPCInITBlock; + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Set %u IT software single breakpoints.", __FUNCTION__, m_sw_single_step_itblock_break_count); + + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: next_pc=0x%8.8x (%s)", __FUNCTION__, next_pc, next_pc_is_thumb ? "Thumb" : "ARM"); + + if (next_pc & 0x1) + { + assert(next_pc_is_thumb); + } + + if (next_pc_is_thumb) + { + next_pc &= ~0x1; + } + else + { + assert((next_pc & 0x3) == 0); + } + + if (!inITBlock || (inITBlock && !m_last_decode_arm.setsFlags) || (lastInITBlock && m_last_decode_arm.branch)) + { + err = KERN_SUCCESS; + +#if defined DNB_ARCH_MACH_ARM_DEBUG_SW_STEP + m_sw_single_step_next_pc = next_pc; + if (next_pc_is_thumb) + m_sw_single_step_next_pc |= 1; // Set bit zero if the next PC is expected to be Thumb +#else + const DNBBreakpoint *bp = m_thread.Process()->Breakpoints().FindByAddress(next_pc); + + if (bp == NULL) + { + m_sw_single_step_break_id = m_thread.Process()->CreateBreakpoint(next_pc, next_pc_is_thumb ? 2 : 4, false, m_thread.GetID()); + if (!LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id)) + err = KERN_INVALID_ARGUMENT; + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: software single step breakpoint with breakID=%d set at 0x%8.8x", __FUNCTION__, m_sw_single_step_break_id, next_pc); + } +#endif + } + + return err.GetError(); +} + +#endif + +MachThreadContext* +MachThreadContext_arm::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread) +{ + return new MachThreadContext_arm(thread); +} + +void +MachThreadContext_arm::Initialize() +{ + ArchSpec arch_spec(CPU_TYPE_ARM, CPU_TYPE_ANY); + ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_arm::Create); +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h new file mode 100644 index 000000000000..4ec72dfd0d46 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h @@ -0,0 +1,63 @@ +//===-- MachThreadContext_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_MachThreadContext_arm_h_ +#define liblldb_MachThreadContext_arm_h_ + +#include "MachThreadContext.h" +#include "RegisterContextMach_arm.h" + +class ThreadMacOSX; + +class MachThreadContext_arm : public MachThreadContext +{ +public: + enum { kMaxNumThumbITBreakpoints = 4 }; + + static MachThreadContext* + Create (const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + static void + Initialize(); + + MachThreadContext_arm(ThreadMacOSX &thread); + + virtual + ~MachThreadContext_arm(); + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const; + + virtual void + InitializeInstance(); + + virtual void + ThreadWillResume (); + + virtual bool + ShouldStop (); + + virtual void + RefreshStateAfterStop (); + + static uint32_t + GetCPUType (); + +protected: + kern_return_t + EnableHardwareSingleStep (bool enable); + +protected: + lldb::addr_t m_hw_single_chained_step_addr; + uint32_t m_bvr0_reg; + uint32_t m_bcr0_reg; + uint32_t m_bvr0_save; + uint32_t m_bcr0_save; +}; +#endif // #ifndef liblldb_MachThreadContext_arm_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp new file mode 100644 index 000000000000..ea148a69f416 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp @@ -0,0 +1,245 @@ +//===-- MachThreadContext_i386.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 (__i386__) || defined (__x86_64__) + + +#include "MachThreadContext_i386.h" + +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" + +#include "ProcessMacOSX.h" +#include "ThreadMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + +MachThreadContext_i386::MachThreadContext_i386 (ThreadMacOSX &thread) : + MachThreadContext (thread), + m_flags_reg(LLDB_INVALID_REGNUM) +{ +} + +MachThreadContext_i386::~MachThreadContext_i386() +{ +} + + +MachThreadContext* +MachThreadContext_i386::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread) +{ + return new MachThreadContext_i386(thread); +} + +// Class init function +void +MachThreadContext_i386::Initialize() +{ + ArchSpec arch_spec("i386"); + ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_i386::Create); +} + +// Instance init function +void +MachThreadContext_i386::InitializeInstance() +{ + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx != NULL); + m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); +} + + +uint32_t +MachThreadContext_i386::GetCPUType() +{ + return CPU_TYPE_I386; +} + +void +MachThreadContext_i386::ThreadWillResume() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping); +} + +bool +MachThreadContext_i386::ShouldStop() +{ + return true; +} + +void +MachThreadContext_i386::RefreshStateAfterStop() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (false); +} + +bool +MachThreadContext_i386::NotifyException (MachException::Data& exc) +{ + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) + { + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); + if (pc != LLDB_INVALID_ADDRESS && pc > 0) + { + pc -= 1; + reg_ctx->SetPC(pc); + } + return true; + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + + +// Set the single step bit in the processor status register. +//kern_return_t +//MachThreadContext_i386::EnableHardwareSingleStep (bool enable) +//{ +// RegisterContext *reg_ctx = m_thread.GetRegisterContext(); +// assert (reg_ctx); +// Scalar rflags_scalar; +// +// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar)) +// { +// Flags rflags(rflags_scalar.UInt()); +// const uint32_t trace_bit = 0x100u; +// if (enable) +// { +// // If the trace bit is already cleared, there is nothing to do +// if (rflags.IsSet (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Set (trace_bit); +// } +// else +// { +// // If the trace bit is already cleared, there is nothing to do +// if (rflags.IsClear (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Clear(trace_bit); +// } +// +// rflags_scalar = rflags.GetAllFlagBits(); +// // If the code makes it here we have changes to the GPRs which +// // we need to write back out, so lets do that. +// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar)) +// return KERN_SUCCESS; +// } +// // Return the error code for reading the GPR registers back +// return KERN_INVALID_ARGUMENT; +//} + +RegisterContext * +MachThreadContext_i386::CreateRegisterContext (StackFrame *frame) const +{ + return new RegisterContextMach_i386(m_thread, frame); +} + + +size_t +MachThreadContext_i386::GetStackFrameData(StackFrame *first_frame, std::vector >& fp_pc_pairs) +{ + fp_pc_pairs.clear(); + + std::pair fp_pc_pair; + + typedef struct Frame_i386 + { + uint32_t fp; + uint32_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + + Frame_i386 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) }; + + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + + const size_t k_frame_size = sizeof(frame); + Error error; + + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (8 bytes) + if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + + if (frame.pc != 0) + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + } + if (!fp_pc_pairs.empty()) + { + lldb::addr_t first_frame_pc = fp_pc_pairs.front().second; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr(); + + if (addr_range_ptr) + { + if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP(0); + // Read the real second frame return address into frame.pc + if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + // Construct a correct second frame (we already read the pc for it above + frame.fp = fp_pc_pairs.front().first; + + // Insert the frame + fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc)); + + // Correct the fp in the first frame to use the SP + fp_pc_pairs.front().first = first_frame_sp; + } + } + } + } + } + return fp_pc_pairs.size(); +} + + +#endif // #if defined (__i386__) diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h new file mode 100644 index 000000000000..4bee2e7be8f7 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h @@ -0,0 +1,57 @@ +//===-- MachThreadContext_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_MachThreadContext_i386_h_ +#define liblldb_MachThreadContext_i386_h_ + +#if defined (__i386__) || defined (__x86_64__) + +#include "MachThreadContext.h" +#include "RegisterContextMach_i386.h" + +class ThreadMacOSX; + +class MachThreadContext_i386 : public MachThreadContext +{ +public: + static MachThreadContext* Create(const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + // Class init function + static void Initialize(); + + MachThreadContext_i386(ThreadMacOSX &thread); + + virtual + ~MachThreadContext_i386(); + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const; + + virtual void InitializeInstance(); + virtual void ThreadWillResume(); + virtual bool ShouldStop (); + virtual void RefreshStateAfterStop(); + + virtual bool NotifyException(MachException::Data& exc); + virtual size_t GetStackFrameData(lldb_private::StackFrame *first_frame, std::vector >& fp_pc_pairs); + static uint32_t GetCPUType(); + +protected: +// kern_return_t EnableHardwareSingleStep (bool enable); + uint32_t m_flags_reg; +private: + DISALLOW_COPY_AND_ASSIGN (MachThreadContext_i386); +}; + +//#if defined (__i386__) +//typedef MachThreadContext_i386 DNBArch; +//#endif + +#endif // defined (__i386__) || defined (__x86_64__) +#endif // #ifndef liblldb_MachThreadContext_i386_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp new file mode 100644 index 000000000000..3ee9eb419de1 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp @@ -0,0 +1,255 @@ +//===-- MachThreadContext_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. +// +//===----------------------------------------------------------------------===// + +#if defined (__i386__) || defined (__x86_64__) + +#include + +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" + +#include "MachThreadContext_x86_64.h" +#include "ProcessMacOSX.h" +#include "ThreadMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + +MachThreadContext_x86_64::MachThreadContext_x86_64(ThreadMacOSX &thread) : + MachThreadContext (thread), + m_flags_reg(LLDB_INVALID_REGNUM) +{ +} + +MachThreadContext_x86_64::~MachThreadContext_x86_64() +{ +} + +MachThreadContext* +MachThreadContext_x86_64::Create(const ArchSpec &arch_spec, ThreadMacOSX &thread) +{ + return new MachThreadContext_x86_64(thread); +} + +// Class init function +void +MachThreadContext_x86_64::Initialize() +{ + ArchSpec arch_spec("x86_64"); + ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_x86_64::Create); +} + +// Instance init function +void +MachThreadContext_x86_64::InitializeInstance() +{ + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx != NULL); + m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); +} + +uint32_t +MachThreadContext_x86_64::GetCPUType() +{ + return CPU_TYPE_X86_64; +} + +void +MachThreadContext_x86_64::ThreadWillResume() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping); +} + +bool +MachThreadContext_x86_64::ShouldStop() +{ + return true; +} + +void +MachThreadContext_x86_64::RefreshStateAfterStop() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (false); +} + +bool +MachThreadContext_x86_64::NotifyException(MachException::Data& exc) +{ + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) + { + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); + if (pc != LLDB_INVALID_ADDRESS && pc > 0) + { + pc -= 1; + reg_ctx->SetPC(pc); + } + return true; + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + + +// Set the single step bit in the processor status register. +//kern_return_t +//MachThreadContext_x86_64::EnableHardwareSingleStep (bool enable) +//{ +// RegisterContext *reg_ctx = m_thread.GetRegisterContext(); +// assert (reg_ctx); +// Scalar rflags_scalar; +// +// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar)) +// { +// Flags rflags(rflags_scalar.UInt()); +// const uint32_t trace_bit = 0x100u; +// if (enable) +// { +// // If the trace bit is already set, there is nothing to do +// if (rflags.IsSet (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Set (trace_bit); +// } +// else +// { +// // If the trace bit is already cleared, there is nothing to do +// if (rflags.IsClear (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Clear(trace_bit); +// } +// +// rflags_scalar = rflags.GetAllFlagBits(); +// // If the code makes it here we have changes to the GPRs which +// // we need to write back out, so lets do that. +// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar)) +// return KERN_SUCCESS; +// } +// // Return the error code for reading the GPR registers back +// return KERN_INVALID_ARGUMENT; +//} +// + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit PowerPC. +//---------------------------------------------------------------------- + + + +RegisterContext * +MachThreadContext_x86_64::CreateRegisterContext (StackFrame *frame) const +{ + return new RegisterContextMach_x86_64(m_thread, frame); +} + + +//bool +//MachThreadContext_x86_64::RegisterSetStateIsValid (uint32_t set) const +//{ +// return m_state.RegisterSetIsCached(set); +//} + + +size_t +MachThreadContext_x86_64::GetStackFrameData(StackFrame *first_frame, std::vector >& fp_pc_pairs) +{ + fp_pc_pairs.clear(); + + std::pair fp_pc_pair; + + typedef struct Frame_x86_64 + { + uint64_t fp; + uint64_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + + Frame_x86_64 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) }; + + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + Error error; + const size_t k_frame_size = sizeof(frame); + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (16 bytes) + if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + + if (frame.pc >= 0x1000) + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + } + if (!fp_pc_pairs.empty()) + { + lldb::addr_t first_frame_pc = fp_pc_pairs.front().second; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr(); + + if (addr_range_ptr) + { + if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP(0); + // Read the real second frame return address into frame.pc + if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + // Construct a correct second frame (we already read the pc for it above + frame.fp = fp_pc_pairs.front().first; + + // Insert the frame + fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc)); + + // Correct the fp in the first frame to use the SP + fp_pc_pairs.front().first = first_frame_sp; + } + } + } + } + } + return fp_pc_pairs.size(); +} + + +#endif // #if defined (__i386__) || defined (__x86_64__) diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h new file mode 100644 index 000000000000..45d5a3750524 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h @@ -0,0 +1,72 @@ +//===-- MachThreadContext_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_MachThreadContext_x86_64_h_ +#define liblldb_MachThreadContext_x86_64_h_ + +#if defined (__i386__) || defined (__x86_64__) + +#include "MachThreadContext.h" +#include "RegisterContextMach_x86_64.h" + +class ThreadMacOSX; + +class MachThreadContext_x86_64 : public MachThreadContext +{ +public: + static MachThreadContext* + Create(const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + // Class init function + static void + Initialize(); + + // Instance init function + void + InitializeInstance(); + + MachThreadContext_x86_64 (ThreadMacOSX &thread); + + virtual + ~MachThreadContext_x86_64(); + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const; + + virtual void + ThreadWillResume (); + + virtual bool + ShouldStop (); + + virtual void + RefreshStateAfterStop (); + + virtual bool + NotifyException (MachException::Data& exc); + + virtual size_t + GetStackFrameData (lldb_private::StackFrame *first_frame, std::vector >& fp_pc_pairs); + + static uint32_t + GetCPUType(); + +protected: +// kern_return_t EnableHardwareSingleStep (bool enable); + uint32_t m_flags_reg; +private: + DISALLOW_COPY_AND_ASSIGN (MachThreadContext_x86_64); +}; + +//#if defined (__x86_64__) +//typedef MachThreadContext_x86_64 DNBArch; +//#endif + +#endif // defined (__i386__) || defined (__x86_64__) +#endif // #ifndef liblldb_MachThreadContext_x86_64_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp new file mode 100644 index 000000000000..c91af3c3596b --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp @@ -0,0 +1,195 @@ +//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachVMMemory.h" + +#include + +#include "MachVMRegion.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb_private; + +MachVMMemory::MachVMMemory() : + m_page_size (kInvalidPageSize) +{ +} + +MachVMMemory::~MachVMMemory() +{ +} + +size_t +MachVMMemory::PageSize(lldb_private::Error &error) +{ + if (m_page_size == kInvalidPageSize) + { + error = ::host_page_size( ::mach_host_self(), &m_page_size); + if (error.Fail()) + m_page_size = 0; + } + + if (m_page_size != 0 && m_page_size != kInvalidPageSize) + { + if (error.Success()) + error.SetErrorString ("unable to determine page size"); + } + return m_page_size; +} + +size_t +MachVMMemory::MaxBytesLeftInPage (lldb::addr_t addr, size_t count) +{ + Error error; + const size_t page_size = PageSize(error); + if (page_size > 0) + { + size_t page_offset = (addr % page_size); + size_t bytes_left_in_page = page_size - page_offset; + if (count > bytes_left_in_page) + count = bytes_left_in_page; + } + return count; +} + +size_t +MachVMMemory::Read(task_t task, lldb::addr_t address, void *data, size_t data_count, Error &error) +{ + if (data == NULL || data_count == 0) + return 0; + + size_t total_bytes_read = 0; + lldb::addr_t curr_addr = address; + uint8_t *curr_data = (uint8_t*)data; + while (total_bytes_read < data_count) + { + mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read); + mach_msg_type_number_t curr_bytes_read = 0; + vm_offset_t vm_memory = NULL; + error = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY|PD_LOG_VERBOSE); + + if (log || error.Fail()) + error.PutToLog (log, "::mach_vm_read (task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i)", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read); + + if (error.Success()) + { + if (curr_bytes_read != curr_size) + { + if (log) + error.PutToLog (log, "::mach_vm_read (task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size); + } + ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read); + ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read); + total_bytes_read += curr_bytes_read; + curr_addr += curr_bytes_read; + curr_data += curr_bytes_read; + } + else + { + break; + } + } + return total_bytes_read; +} + + +size_t +MachVMMemory::Write(task_t task, lldb::addr_t address, const void *data, size_t data_count, Error &error) +{ + MachVMRegion vmRegion(task); + + size_t total_bytes_written = 0; + lldb::addr_t curr_addr = address; + const uint8_t *curr_data = (const uint8_t*)data; + + + while (total_bytes_written < data_count) + { + if (vmRegion.GetRegionForAddress(curr_addr)) + { + mach_vm_size_t curr_data_count = data_count - total_bytes_written; + mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr); + if (region_bytes_left == 0) + { + break; + } + if (curr_data_count > region_bytes_left) + curr_data_count = region_bytes_left; + + if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE)) + { + size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count, error); + if (bytes_written <= 0) + { + // Error should have already be posted by WriteRegion... + break; + } + else + { + total_bytes_written += bytes_written; + curr_addr += bytes_written; + curr_data += bytes_written; + } + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count)); + break; + } + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address); + break; + } + } + + return total_bytes_written; +} + + +size_t +MachVMMemory::WriteRegion(task_t task, const lldb::addr_t address, const void *data, const size_t data_count, Error &error) +{ + if (data == NULL || data_count == 0) + return 0; + + size_t total_bytes_written = 0; + lldb::addr_t curr_addr = address; + const uint8_t *curr_data = (const uint8_t*)data; + while (total_bytes_written < data_count) + { + mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written); + error = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + if (log || error.Fail()) + error.PutToLog (log, "::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count); + +#if defined (__powerpc__) || defined (__ppc__) + vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH; + + error = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value); + if (log || error.Fail()) + error.Log(log, "::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count); +#endif + + if (error.Success()) + { + total_bytes_written += curr_data_count; + curr_addr += curr_data_count; + curr_data += curr_data_count; + } + else + { + break; + } + } + return total_bytes_written; +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h new file mode 100644 index 000000000000..866fa3697063 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h @@ -0,0 +1,36 @@ +//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MachVMMemory_h_ +#define liblldb_MachVMMemory_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" + +class MachVMMemory +{ +public: + enum { kInvalidPageSize = ~0 }; + MachVMMemory(); + ~MachVMMemory(); + size_t Read(task_t task, lldb::addr_t address, void *data, size_t data_count, lldb_private::Error &error); + size_t Write(task_t task, lldb::addr_t address, const void *data, size_t data_count, lldb_private::Error &error); + size_t PageSize(lldb_private::Error &error); + +protected: + size_t MaxBytesLeftInPage(lldb::addr_t addr, size_t count); + + size_t WriteRegion(task_t task, const lldb::addr_t address, const void *data, const size_t data_count, lldb_private::Error &error); + vm_size_t m_page_size; +}; + + +#endif // #ifndef liblldb_MachVMMemory_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp new file mode 100644 index 000000000000..87044fb31b26 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp @@ -0,0 +1,183 @@ +//===-- MachVMRegion.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 "MachVMRegion.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb_private; + +MachVMRegion::MachVMRegion(task_t task) : + m_task(task), + m_addr(LLDB_INVALID_ADDRESS), + m_err(), + m_start(LLDB_INVALID_ADDRESS), + m_size(0), + m_depth(-1), + m_data(), + m_curr_protection(0), + m_protection_addr(LLDB_INVALID_ADDRESS), + m_protection_size(0) +{ + memset(&m_data, 0, sizeof(m_data)); +} + +MachVMRegion::~MachVMRegion() +{ + // Restore any original protections and clear our vars + Clear(); +} + +void +MachVMRegion::Clear() +{ + RestoreProtections(); + m_addr = LLDB_INVALID_ADDRESS; + m_err.Clear(); + m_start = LLDB_INVALID_ADDRESS; + m_size = 0; + m_depth = -1; + memset(&m_data, 0, sizeof(m_data)); + m_curr_protection = 0; + m_protection_addr = LLDB_INVALID_ADDRESS; + m_protection_size = 0; +} + +bool +MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot) +{ + if (ContainsAddress(addr)) + { + mach_vm_size_t prot_size = size; + mach_vm_address_t end_addr = EndAddress(); + if (prot_size > (end_addr - addr)) + prot_size = end_addr - addr; + + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS); + if (prot_size > 0) + { + if (prot == (m_curr_protection & VM_PROT_ALL)) + { + if (log) + log->Printf ("MachVMRegion::%s: protections (%u) already sufficient for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, prot, m_task, (uint64_t)addr); + // Protections are already set as requested... + return true; + } + else + { + m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot); + if (m_err.Fail()) + { + // Try again with the ability to create a copy on write region + m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot | VM_PROT_COPY); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot | VM_PROT_COPY); + } + if (m_err.Success()) + { + m_curr_protection = prot; + m_protection_addr = addr; + m_protection_size = prot_size; + return true; + } + } + } + else + { + log->Printf("MachVMRegion::%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, m_task, (uint64_t)addr); + } + } + return false; +} + +bool +MachVMRegion::RestoreProtections() +{ + if (m_curr_protection != m_data.protection && m_protection_size > 0) + { + m_err = ::mach_vm_protect (m_task, m_protection_addr, m_protection_size, 0, m_data.protection); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)m_protection_addr, (uint64_t)m_protection_size, 0, m_data.protection); + if (m_err.Success()) + { + m_protection_size = 0; + m_protection_addr = LLDB_INVALID_ADDRESS; + m_curr_protection = m_data.protection; + return true; + } + } + else + { + m_err.Clear(); + return true; + } + + return false; +} + +bool +MachVMRegion::GetRegionForAddress(lldb::addr_t addr) +{ + // Restore any original protections and clear our vars + Clear(); + m_addr = addr; + m_start = addr; + m_depth = 1024; + mach_msg_type_number_t info_size = kRegionInfoSize; + assert(sizeof(info_size) == 4); + m_err = ::mach_vm_region_recurse (m_task, &m_start, &m_size, &m_depth, (vm_region_recurse_info_t)&m_data, &info_size); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_region_recurse ( task = 0x%4.4x, address => 0x%8.8llx, size => %llu, nesting_depth => %d, info => %p, infoCnt => %d) addr = 0x%8.8llx ", m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth, &m_data, info_size, (uint64_t)addr); + if (m_err.Fail()) + { + return false; + } + else + { + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + { + log->Printf("info = { prot = %u, " + "max_prot = %u, " + "inheritance = 0x%8.8x, " + "offset = 0x%8.8llx, " + "user_tag = 0x%8.8x, " + "ref_count = %u, " + "shadow_depth = %u, " + "ext_pager = %u, " + "share_mode = %u, " + "is_submap = %d, " + "behavior = %d, " + "object_id = 0x%8.8x, " + "user_wired_count = 0x%4.4x }", + m_data.protection, + m_data.max_protection, + m_data.inheritance, + (uint64_t)m_data.offset, + m_data.user_tag, + m_data.ref_count, + m_data.shadow_depth, + m_data.external_pager, + m_data.share_mode, + m_data.is_submap, + m_data.behavior, + m_data.object_id, + m_data.user_wired_count); + } + } + + m_curr_protection = m_data.protection; + + return true; +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h new file mode 100644 index 000000000000..b12353df823e --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h @@ -0,0 +1,63 @@ +//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MachVMRegion_h_ +#define liblldb_MachVMRegion_h_ + +#include +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" + +class MachVMRegion +{ +public: + MachVMRegion(task_t task); + ~MachVMRegion(); + + void Clear(); + mach_vm_address_t StartAddress() const { return m_start; } + mach_vm_address_t EndAddress() const { return m_start + m_size; } + mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const + { + if (ContainsAddress(addr)) + return m_size - (addr - m_start); + else + return 0; + } + bool ContainsAddress(mach_vm_address_t addr) const + { + return addr >= StartAddress() && addr < EndAddress(); + } + + bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot); + bool RestoreProtections(); + bool GetRegionForAddress(lldb::addr_t addr); +protected: +#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64) + typedef vm_region_submap_short_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; +#else + typedef vm_region_submap_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 }; +#endif + + task_t m_task; + mach_vm_address_t m_addr; + lldb_private::Error m_err; + mach_vm_address_t m_start; + mach_vm_size_t m_size; + natural_t m_depth; + RegionInfo m_data; + vm_prot_t m_curr_protection; // The current, possibly modified protections. Original value is saved in m_data.protections. + mach_vm_address_t m_protection_addr; // The start address at which protections were changed + mach_vm_size_t m_protection_size; // The size of memory that had its protections changed + +}; + +#endif // #ifndef liblldb_MachVMRegion_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs new file mode 100644 index 000000000000..7f16fe133567 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs @@ -0,0 +1,16 @@ +/* + * nub.defs + */ + +/* + * DNBConfig.h is autogenerated by a perl script that is run as a build + * script in XCode. XCode is responsible for calling the script and setting + * the include paths correctly to locate it. The file will exist in the + * derived sources directory in the build folder. + * + */ + +#include "DNBConfig.h" + + +#include diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp new file mode 100644 index 000000000000..3c37b64dc461 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp @@ -0,0 +1,2228 @@ +//===-- ProcessMacOSX.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 +#include +#include + +// Other libraries and framework includes + +#include "lldb/Breakpoint/WatchpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "PseudoTerminal.h" + +#if defined (__arm__) + +#include +#include +#include + +#endif // #if defined (__arm__) + +// Project includes +#include "lldb/Host/Host.h" +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" +#include "ThreadMacOSX.h" + + +#if 0 +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +#ifndef MACH_PROCESS_USE_POSIX_SPAWN +#define MACH_PROCESS_USE_POSIX_SPAWN 1 +#endif + + +#if defined (__arm__) + +static bool +IsSBProcess (lldb::pid_t pid) +{ + bool opt_runningApps = true; + bool opt_debuggable = false; + + CFReleaser sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); + if (sbsAppIDs.get() != NULL) + { + CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); + CFIndex i = 0; + for (i = 0; i < count; i++) + { + CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); + + // Get the process id for the app (if there is one) + lldb::pid_t sbs_pid = LLDB_INVALID_PROCESS_ID; + if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) + { + if (sbs_pid == pid) + return true; + } + } + } + return false; +} + + +#endif // #if defined (__arm__) + +using namespace lldb; +using namespace lldb_private; +// +//void * +//ProcessMacOSX::WaitForChildProcessToExit (void *pid_ptr) +//{ +// const lldb::pid_t pid = *((lldb::user_id_t *)pid_ptr); +// +// Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); +// +// if (log) +// log->Printf ("ProcessMacOSX::%s (arg = %p) thread starting...", __FUNCTION__, pid_ptr); +// +// int status = -1; +// +// while (1) +// { +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0)...", pid, &status); +// +// lldb::pid_t return_pid = ::waitpid (pid, &status, 0); +// +// if (return_pid < 0) +// { +// if (log) +// log->Printf("%s ::waitpid (pid = %i, stat_loc = %p, options = 0) => errno = %i, status = 0x%8.8x", pid, &status, errno, status); +// break; +// } +// +// bool set_exit_status = false; +// if (WIFSTOPPED(status)) +// { +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (STOPPED)", pid, &status, return_pid, status); +// } +// else if (WIFEXITED(status)) +// { +// set_exit_status = true; +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (EXITED)", pid, &status, return_pid, status); +// } +// else if (WIFSIGNALED(status)) +// { +// set_exit_status = true; +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (SIGNALED)", pid, &status, return_pid, status); +// } +// else +// { +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x", pid, &status, return_pid, status); +// } +// +// if (set_exit_status) +// { +// // Try and deliver the news to the process if it is still around +// TargetSP target_sp(TargetList::SharedList().FindTargetWithProcessID (return_pid)); +// if (target_sp.get()) +// { +// ProcessMacOSX *process = dynamic_cast(target_sp->GetProcess().get()); +// if (process) +// { +// process->SetExitStatus (status); +// if (log) +// log->Printf("Setting exit status of %i to 0x%8.8x", pid, status); +// process->Task().ShutDownExceptionThread(); +// } +// } +// // Break out of the loop and return. +// break; +// } +// } +// +// if (log) +// log->Printf ("ProcessMacOSX::%s (arg = %p) thread exiting...", __FUNCTION__, pid_ptr); +// +// return NULL; +//} +// + +const char * +ProcessMacOSX::GetPluginNameStatic() +{ + return "process.macosx"; +} + +const char * +ProcessMacOSX::GetPluginDescriptionStatic() +{ + return "Native MacOSX user process debugging plug-in."; +} + +void +ProcessMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessMacOSX::CreateInstance); +} + + +Process* +ProcessMacOSX::CreateInstance (Target &target, Listener &listener) +{ + ProcessMacOSX::Initialize(); + + return new ProcessMacOSX (target, listener); +} + +bool +ProcessMacOSX::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +//---------------------------------------------------------------------- +// ProcessMacOSX constructor +//---------------------------------------------------------------------- +ProcessMacOSX::ProcessMacOSX(Target& target, Listener &listener) : + Process (target, listener), + m_dynamic_loader_ap (), +// m_wait_thread (LLDB_INVALID_HOST_THREAD), + m_byte_order (eByteOrderHost), + m_stdio_ours (false), + m_child_stdin (-1), + m_child_stdout (-1), + m_child_stderr (-1), + m_task (this), + m_flags (eFlagsNone), + m_stdio_thread (LLDB_INVALID_HOST_THREAD), + m_stdio_mutex (Mutex::eMutexTypeRecursive), + m_stdout_data (), + m_exception_messages (), + m_exception_messages_mutex (Mutex::eMutexTypeRecursive), + m_arch_spec () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessMacOSX::~ProcessMacOSX() +{ +// m_mach_process.UnregisterNotificationCallbacks (this); + Clear(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +const char * +ProcessMacOSX::GetPluginName() +{ + return "Process debugging plug-in for MacOSX"; +} + +const char * +ProcessMacOSX::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessMacOSX::GetPluginVersion() +{ + return 1; +} + +void +ProcessMacOSX::GetPluginCommandHelp (const char *command, Stream *strm) +{ + strm->Printf("The following arguments can be supplied to the 'log %s' command:\n", GetShortPluginName()); + strm->PutCString("\tverbose - enable verbose logging\n"); + strm->PutCString("\tprocess - enable process logging\n"); + strm->PutCString("\tthread - enable thread logging\n"); + strm->PutCString("\texceptions - enable exception logging\n"); + strm->PutCString("\tdynamic - enable DynamicLoader logging\n"); + strm->PutCString("\tmemory-calls - enable memory read and write call logging\n"); + strm->PutCString("\tmemory-data-short - log short memory read and write byte data\n"); + strm->PutCString("\tmemory-data-long - log all memory read and write byte data\n"); + strm->PutCString("\tmemory-protections - log memory protection calls\n"); + strm->PutCString("\tbreakpoints - log breakpoint calls\n"); + strm->PutCString("\twatchpoints - log watchpoint calls\n"); + strm->PutCString("\tevents - log event and event queue status\n"); + strm->PutCString("\tstep - log step related activity\n"); + strm->PutCString("\ttask - log task functions\n"); +} + +Error +ProcessMacOSX::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ProcessMacOSX::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessMacOSX::DoLaunch +( + Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ +// ::LogSetBitMask (PD_LOG_DEFAULT); +// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); +// ::LogSetLogFile ("/dev/stdout"); + + Error error; + ObjectFile * object_file = module->GetObjectFile(); + if (object_file) + { + ArchSpec arch_spec(module->GetArchitecture()); + + // Set our user ID to our process ID. + SetID (LaunchForDebug(argv[0], argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, eLaunchDefault, error)); + } + else + { + // Set our user ID to an invalid process ID. + SetID (LLDB_INVALID_PROCESS_ID); + error.SetErrorToGenericError (); + error.SetErrorStringWithFormat("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString()); + } + + // Return the process ID we have + return error; +} + +Error +ProcessMacOSX::DoAttach (lldb::pid_t attach_pid) +{ + Error error; + + // Clear out and clean up from any current state + Clear(); + // HACK: require arch be set correctly at the target level until we can + // figure out a good way to determine the arch of what we are attaching to + m_arch_spec = m_target.GetArchitecture(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + SetPrivateState (eStateAttaching); + SetID(attach_pid); + // Let ourselves know we are going to be using SBS if the correct flag bit is set... +#if defined (__arm__) + if (IsSBProcess(pid)) + m_flags |= eFlagsUsingSBS; +#endif + + if (Task().GetTaskPortForProcessID(error) == TASK_NULL) + { + if (log) + log->Printf ("error attaching to pid %i: %s", GetID(), error.AsCString()); + + } + else + { + Task().StartExceptionThread(error); + + if (error.Success()) + { + errno = 0; + if (::ptrace (PT_ATTACHEXC, GetID(), 0, 0) == 0) + { + m_flags.Set (eFlagsAttached); + // Sleep a bit to let the exception get received and set our process status + // to stopped. + ::usleep(250000); + + if (log) + log->Printf ("successfully attached to pid %d", GetID()); + return error; + } + else + { + error.SetErrorToErrno(); + if (log) + log->Printf ("error: failed to attach to pid %d", GetID()); + } + } + else + { + if (log) + log->Printf ("error: failed to start exception thread for pid %d: %s", GetID(), error.AsCString()); + } + + } + } + SetID (LLDB_INVALID_PROCESS_ID); + if (error.Success()) + error.SetErrorStringWithFormat ("failed to attach to pid %d", attach_pid); + return error; +} + +Error +ProcessMacOSX::WillLaunchOrAttach () +{ + Error error; + // TODO: this is hardcoded for macosx right now. We need this to be more dynamic + m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.macosx-dyld")); + + if (m_dynamic_loader_ap.get() == NULL) + error.SetErrorString("unable to find the dynamic loader named 'dynamic-loader.macosx-dyld'"); + + return error; +} + + +Error +ProcessMacOSX::WillLaunch (Module* module) +{ + return WillLaunchOrAttach (); +} + +void +ProcessMacOSX::DidLaunchOrAttach () +{ + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + m_dynamic_loader_ap.reset(); + } + else + { + Module * exe_module = GetTarget().GetExecutableModule ().get(); + assert (exe_module); + + m_arch_spec = exe_module->GetArchitecture(); + assert (m_arch_spec.IsValid()); + + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + assert (exe_objfile); + + m_byte_order = exe_objfile->GetByteOrder(); + assert (m_byte_order != eByteOrderInvalid); + // Install a signal handler so we can catch when our child process + // dies and set the exit status correctly. + + Host::StartMonitoringChildProcess (Process::SetProcessExitStatus, NULL, GetID(), false); + + if (m_arch_spec == ArchSpec("arm")) + { + // On ARM we want the actual target triple of the OS to get the + // most capable ARM slice for the process. Since this plug-in is + // only used for doing native debugging this will work. + m_target_triple = Host::GetTargetTriple(); + } + else + { + // We want the arch of the process, and the vendor and OS from the + // host OS. + StreamString triple; + + triple.Printf("%s-%s-%s", + m_arch_spec.AsCString(), + Host::GetVendorString().AsCString("apple"), + Host::GetOSString().AsCString("darwin")); + + m_target_triple.SetCString(triple.GetString().c_str()); + } + } +} + +void +ProcessMacOSX::DidLaunch () +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::DidLaunch()"); + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidLaunch(); +} + +void +ProcessMacOSX::DidAttach () +{ + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidAttach(); +} + +Error +ProcessMacOSX::WillAttach (lldb::pid_t pid) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessMacOSX::DoResume () +{ + Error error; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::Resume()"); + const StateType state = m_private_state.GetValue(); + + if (CanResume(state)) + { + error = PrivateResume(LLDB_INVALID_THREAD_ID); + } + else if (state == eStateRunning) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", Task().GetTaskPort()); + } + else + { + error.SetErrorStringWithFormat("task 0x%x can't continue, ignoring...", Task().GetTaskPort()); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", Task().GetTaskPort()); + } + return error; +} + +size_t +ProcessMacOSX::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site) +{ + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + + switch (m_arch_spec.GetCPUType()) + { + case CPU_TYPE_ARM: + // TODO: fill this in for ARM. We need to dig up the symbol for + // the address in the breakpoint locaiton and figure out if it is + // an ARM or Thumb breakpoint. + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + break; + + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + break; + + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + break; + + default: + assert(!"Unhandled architecture in ProcessMacOSX::GetSoftwareBreakpointTrapOpcode()"); + return 0; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + return 0; +} +uint32_t +ProcessMacOSX::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ProcessMacOSX::%s (pid = %4.4x)", __FUNCTION__, GetID()); + + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) + { + // Update the thread list's stop id immediately so we don't recurse into this function. + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + task_t task = Task().GetTaskPort(); + Error err(::task_threads (task, &thread_list, &thread_list_count), eErrorTypeMachKernel); + + if (log || err.Fail()) + err.PutToLog(log, "::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); + + if (err.GetError() == KERN_SUCCESS && thread_list_count > 0) + { + ThreadList curr_thread_list (this); + curr_thread_list.SetStopID(stop_id); + + size_t idx; + // Iterator through the current thread list and see which threads + // we already have in our list (keep them), which ones we don't + // (add them), and which ones are not around anymore (remove them). + for (idx = 0; idx < thread_list_count; ++idx) + { + const lldb::tid_t tid = thread_list[idx]; + ThreadSP thread_sp(GetThreadList().FindThreadByID (tid, false)); + if (thread_sp.get() == NULL) + thread_sp.reset (new ThreadMacOSX (*this, tid)); + curr_thread_list.AddThread(thread_sp); + } + + m_thread_list = curr_thread_list; + + // Free the vm memory given to us by ::task_threads() + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (lldb::tid_t)); + ::vm_deallocate (::mach_task_self(), + (vm_address_t)thread_list, + thread_list_size); + } + } + return GetThreadList().GetSize(false); +} + + +void +ProcessMacOSX::RefreshStateAfterStop () +{ + // If we are attaching, let our dynamic loader plug-in know so it can get + // an initial list of shared libraries. + + // We must be attaching if we don't already have a valid architecture + if (!m_arch_spec.IsValid()) + { + Module *exe_module = GetTarget().GetExecutableModule().get(); + if (exe_module) + m_arch_spec = exe_module->GetArchitecture(); + } + // Discover new threads: + UpdateThreadListIfNeeded (); + + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); + + // Let each thread know of any exceptions + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + task_t task = Task().GetTaskPort(); + size_t i; + for (i=0; iNotifyException (m_exception_messages[i].state); + } + } + if (log) + m_exception_messages[i].PutToLog(log); + } + +} + +Error +ProcessMacOSX::DoHalt () +{ + return Signal (SIGSTOP); +} + +Error +ProcessMacOSX::WillDetach () +{ + Error error; + const StateType state = m_private_state.GetValue(); + + if (IsRunning(state)) + { + error.SetErrorToGenericError(); + error.SetErrorString("Process must be stopped in order to detach."); + } + return error; +} + +Error +ProcessMacOSX::DoSIGSTOP (bool clear_all_breakpoints) +{ + Error error; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + + if (log) + log->Printf ("ProcessMacOSX::DoSIGSTOP()"); + EventSP event_sp; + TimeValue timeout_time; + + StateType state = m_private_state.GetValue(); + + lldb::pid_t pid = GetID(); + + if (IsRunning(state)) + { + // If our process is running, we need to SIGSTOP it so we can detach. + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() - kill (%i, SIGSTOP)", pid); + + // Send the SIGSTOP and wait a few seconds for it to stop + + // Pause the Private State Thread so it doesn't intercept the events we need to wait for. + PausePrivateStateThread(); + + m_thread_list.DiscardThreadPlans(); + + // First jettison all the current thread plans, since we want to make sure it + // really just stops. + + if (::kill (pid, SIGSTOP) == 0) + error.Clear(); + else + error.SetErrorToErrno(); + + if (error.Fail()) + error.PutToLog(log, "::kill (pid = %i, SIGSTOP)", pid); + + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + + state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + + // Resume the private state thread at this point. + ResumePrivateStateThread(); + + if (!StateIsStoppedState (state)) + { + if (log) + log->Printf("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP"); + return error; + } + if (clear_all_breakpoints) + GetTarget().DisableAllBreakpoints(); + } + else if (!HasExited(state)) + { + if (clear_all_breakpoints) + GetTarget().DisableAllBreakpoints(); + +// const uint32_t num_threads = GetNumThreads(); +// for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) +// { +// Thread *thread = GetThreadAtIndex(thread_idx); +// thread->SetResumeState(eStateRunning); +// if (thread_idx == 0) +// thread->SetResumeSignal(SIGSTOP); +// } + + // Our process was stopped, so resume it and then SIGSTOP it so we can + // detach. + // But discard all the thread plans first, so we don't keep going because we + // are in mid-plan. + + // Pause the Private State Thread so it doesn't intercept the events we need to wait for. + PausePrivateStateThread(); + + m_thread_list.DiscardThreadPlans(); + + if (::kill (pid, SIGSTOP) == 0) + error.Clear(); + else + error.SetErrorToErrno(); + + if (log || error.Fail()) + error.PutToLog(log, "ProcessMacOSX::DoSIGSTOP() ::kill (pid = %i, SIGSTOP)", pid); + + error = PrivateResume(LLDB_INVALID_THREAD_ID); + + // Wait a few seconds for our process to resume + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + + // Make sure the process resumed + if (StateIsStoppedState (state)) + { + if (log) + log->Printf ("ProcessMacOSX::DoSIGSTOP() couldn't resume process, state = %s", StateAsCString(state)); + error.SetErrorStringWithFormat("ProcessMacOSX::DoSIGSTOP() couldn't resume process, state = %s", StateAsCString(state)); + } + else + { + // Send the SIGSTOP and wait a few seconds for it to stop + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + if (!StateIsStoppedState (state)) + { + if (log) + log->Printf("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP"); + error.SetErrorString("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP"); + } + } + // Resume the private state thread at this point. + ResumePrivateStateThread(); + } + + return error; +} + +Error +ProcessMacOSX::DoDestroy () +{ + Error error; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::DoDestroy()"); + + error = DoSIGSTOP (true); + if (error.Success()) + { + StopSTDIOThread(true); + + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() DoSIGSTOP succeeded"); + const StateType state = m_private_state.GetValue(); + // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP + // exception). + { + Mutex::Locker locker(m_exception_messages_mutex); + ReplyToAllExceptions(); + } + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() replied to all exceptions"); + + // Shut down the exception thread and cleanup our exception remappings + Task().ShutDownExceptionThread(); + + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() exception thread has been shutdown"); + + if (!HasExited(state)) + { + lldb::pid_t pid = GetID(); + + // Detach from our process while we are stopped. + errno = 0; + + // Detach from our process + ::ptrace (PT_KILL, pid, 0, 0); + + error.SetErrorToErrno(); + + if (log || error.Fail()) + error.PutToLog (log, "::ptrace (PT_KILL, %u, 0, 0)", pid); + + // Resume our task and let the SIGKILL do its thing. The thread named + // "ProcessMacOSX::WaitForChildProcessToExit(void*)" will catch the + // process exiting, so we don't need to set our state to exited in this + // function. + Task().Resume(); + } + + // NULL our task out as we have already retored all exception ports + Task().Clear(); + + // Clear out any notion of the process we once were + Clear(); + } + return error; +} + +ByteOrder +ProcessMacOSX::GetByteOrder () const +{ + return m_byte_order; +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessMacOSX::IsAlive () +{ + return MachTask::IsValid (Task().GetTaskPort()); +} + +lldb::addr_t +ProcessMacOSX::GetImageInfoAddress() +{ + return Task().GetDYLDAllImageInfosAddress(); +} + +DynamicLoader * +ProcessMacOSX::GetDynamicLoader() +{ + return m_dynamic_loader_ap.get(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ + +size_t +ProcessMacOSX::DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error& error) +{ + return Task().ReadMemory(addr, buf, size, error); +} + +size_t +ProcessMacOSX::DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error& error) +{ + return Task().WriteMemory(addr, buf, size, error); +} + +lldb::addr_t +ProcessMacOSX::DoAllocateMemory (size_t size, uint32_t permissions, Error& error) +{ + return Task().AllocateMemory (size, permissions, error); +} + +Error +ProcessMacOSX::DoDeallocateMemory (lldb::addr_t ptr) +{ + return Task().DeallocateMemory (ptr); +} + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ + +size_t +ProcessMacOSX::GetSTDOUT (char *buf, size_t buf_size, Error &error) +{ + error.Clear(); + Mutex::Locker locker(m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + + //ResetEventBits(eBroadcastBitSTDOUT); + } + } + return bytes_available; +} + +size_t +ProcessMacOSX::GetSTDERR (char *buf, size_t buf_size, Error &error) +{ + error.Clear(); + return 0; +} + +size_t +ProcessMacOSX::PutSTDIN (const char *buf, size_t buf_size, Error &error) +{ + if (m_child_stdin == -1) + { + error.SetErrorString ("Invalid child stdin handle."); + } + else + { + ssize_t bytes_written = ::write (m_child_stdin, buf, buf_size); + if (bytes_written == -1) + error.SetErrorToErrno(); + else + { + error.Clear(); + return bytes_written; + } + } + return 0; +} + +Error +ProcessMacOSX::EnableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + const lldb::addr_t addr = bp_site->GetLoadAddress(); + const lldb::user_id_t site_id = bp_site->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessMacOSX::EnableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("ProcessMacOSX::EnableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already enabled)", site_id, (uint64_t)addr); + return error; + } + + if (bp_site->HardwarePreferred()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp_site->GetThreadID()).get(); + if (thread) + { + bp_site->SetHardwareIndex (thread->SetHardwareBreakpoint(bp_site)); + if (bp_site->IsHardware()) + { + bp_site->SetEnabled(true); + return error; + } + } + } + + // Just let lldb::Process::EnableSoftwareBreakpoint() handle everything... + return EnableSoftwareBreakpoint (bp_site); +} + +Error +ProcessMacOSX::DisableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + const lldb::addr_t addr = bp_site->GetLoadAddress(); + const lldb::user_id_t site_id = bp_site->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessMacOSX::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr); + + if (bp_site->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp_site->GetThreadID()).get(); + if (thread) + { + if (thread->ClearHardwareBreakpoint(bp_site)) + { + bp_site->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSX::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (hardware)", site_id, (uint64_t)addr); + return error; + } + } + error.SetErrorString("hardware breakpoints are no supported"); + return error; + } + + // Just let lldb::Process::EnableSoftwareBreakpoint() handle everything... + return DisableSoftwareBreakpoint (bp_site); +} + +Error +ProcessMacOSX::EnableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + lldb::addr_t addr = wp->GetLoadAddress(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + if (log) + log->Printf ("ProcessMacOSX::EnableWatchpoint(watchID = %d)", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessMacOSX::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return error; + } + else + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + wp->SetHardwareIndex (thread->SetHardwareWatchpoint (wp)); + if (wp->IsHardware ()) + { + wp->SetEnabled(true); + return error; + } + } + else + { + error.SetErrorString("Watchpoints currently only support thread specific watchpoints."); + } + } + } + return error; +} + +Error +ProcessMacOSX::DisableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + + lldb::addr_t addr = wp->GetLoadAddress(); + if (log) + log->Printf ("ProcessMacOSX::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr); + + if (wp->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + if (thread->ClearHardwareWatchpoint (wp)) + { + wp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSX::Disablewatchpoint (watchID = %d) addr = 0x%8.8llx (hardware) => success", watchID, (uint64_t)addr); + return error; + } + } + } + // TODO: clear software watchpoints if we implement them + error.SetErrorToGenericError(); + } + else + { + error.SetErrorString("Watchpoint location argument was NULL."); + } + return error; +} + + +static ProcessMacOSX::CreateArchCalback +ArchCallbackMap(const ArchSpec& arch_spec, ProcessMacOSX::CreateArchCalback callback, bool add ) +{ + // We must wrap the "g_arch_map" file static in a function to avoid + // any global constructors so we don't get a build verification error + typedef std::multimap ArchToProtocolMap; + static ArchToProtocolMap g_arch_map; + + if (add) + { + g_arch_map.insert(std::make_pair(arch_spec, callback)); + return callback; + } + else + { + ArchToProtocolMap::const_iterator pos = g_arch_map.find(arch_spec); + if (pos != g_arch_map.end()) + { + return pos->second; + } + } + return NULL; +} + +void +ProcessMacOSX::AddArchCreateCallback(const ArchSpec& arch_spec, CreateArchCalback callback) +{ + ArchCallbackMap (arch_spec, callback, true); +} + +ProcessMacOSX::CreateArchCalback +ProcessMacOSX::GetArchCreateCallback() +{ + return ArchCallbackMap (m_arch_spec, NULL, false); +} + +void +ProcessMacOSX::Clear() +{ + // Clear any cached thread list while the pid and task are still valid + + Task().Clear(); + // Now clear out all member variables + CloseChildFileDescriptors(); + + m_flags = eFlagsNone; + m_thread_list.Clear(); + { + Mutex::Locker locker(m_exception_messages_mutex); + m_exception_messages.clear(); + } + +} + +bool +ProcessMacOSX::StartSTDIOThread() +{ + // If we created and own the child STDIO file handles, then we track the + // STDIO ourselves, else we let whomever owns these file handles track + // the IO themselves. + if (m_stdio_ours) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s ( )", __FUNCTION__); + // Create the thread that watches for the child STDIO + m_stdio_thread = Host::ThreadCreate ("", ProcessMacOSX::STDIOThread, this, NULL); + return m_stdio_thread != LLDB_INVALID_HOST_THREAD; + } + return false; +} + + +void +ProcessMacOSX::StopSTDIOThread(bool close_child_fds) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s ( )", __FUNCTION__); + // Stop the stdio thread + if (m_stdio_thread != LLDB_INVALID_HOST_THREAD) + { + Host::ThreadCancel (m_stdio_thread, NULL); + thread_result_t result = NULL; + Host::ThreadJoin (m_stdio_thread, &result, NULL); + if (close_child_fds) + CloseChildFileDescriptors(); + else + { + // We may have given up control of these file handles, so just + // set them to invalid values so the STDIO thread can exit when + // we interrupt it with pthread_cancel... + m_child_stdin = -1; + m_child_stdout = -1; + m_child_stderr = -1; + } + } +} + + +void * +ProcessMacOSX::STDIOThread(void *arg) +{ + ProcessMacOSX *proc = (ProcessMacOSX*) arg; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::%s (arg = %p) thread starting...", __FUNCTION__, arg); + + // We start use a base and more options so we can control if we + // are currently using a timeout on the mach_msg. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main thread loop + // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + Error err; + int stdout_fd = proc->GetStdoutFileDescriptor(); + int stderr_fd = proc->GetStderrFileDescriptor(); + if (stdout_fd == stderr_fd) + stderr_fd = -1; + + while (stdout_fd >= 0 || stderr_fd >= 0) + { + //::pthread_testcancel (); + + fd_set read_fds; + FD_ZERO (&read_fds); + if (stdout_fd >= 0) + FD_SET (stdout_fd, &read_fds); + if (stderr_fd >= 0) + FD_SET (stderr_fd, &read_fds); + int nfds = std::max(stdout_fd, stderr_fd) + 1; + + int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); + if (log) + log->Printf("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + + if (num_set_fds < 0) + { + int select_errno = errno; + if (log) + { + err.SetError (select_errno, eErrorTypePOSIX); + err.LogIfError(log, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + } + + switch (select_errno) + { + case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO + break; + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return NULL; + break; + case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + break; + } + } + else if (num_set_fds == 0) + { + } + else + { + char s[1024]; + s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination + int bytes_read = 0; + if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) + { + do + { + bytes_read = ::read (stdout_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); + stdout_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + + if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) + { + do + { + bytes_read = ::read (stderr_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); + stderr_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + } + } + + if (log) + log->Printf("ProcessMacOSX::%s (%p): thread exiting...", __FUNCTION__, arg); + + return NULL; +} + +Error +ProcessMacOSX::DoSignal (int signal) +{ + Error error; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::DoSignal (signal = %d)", signal); + if (::kill (GetID(), signal) != 0) + { + error.SetErrorToErrno(); + error.LogIfError(log, "ProcessMacOSX::DoSignal (%d)", signal); + } + return error; +} + + +Error +ProcessMacOSX::DoDetach() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::DoDetach()"); + + Error error (DoSIGSTOP (true)); + if (error.Success()) + { + CloseChildFileDescriptors (); + + // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP + // exception). + { + Mutex::Locker locker(m_exception_messages_mutex); + ReplyToAllExceptions(); + } + + // Shut down the exception thread and cleanup our exception remappings + Task().ShutDownExceptionThread(); + + lldb::pid_t pid = GetID(); + + // Detach from our process while we are stopped. + errno = 0; + + // Detach from our process + ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + + error.SetErrorToErrno(); + + if (log || error.Fail()) + error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + + // Resume our task + Task().Resume(); + + // NULL our task out as we have already retored all exception ports + Task().Clear(); + + // Clear out any notion of the process we once were + Clear(); + + SetPrivateState (eStateDetached); + } + return error; +} + + + +Error +ProcessMacOSX::ReplyToAllExceptions() +{ + Error error; + Mutex::Locker locker(m_exception_messages_mutex); + if (m_exception_messages.empty() == false) + { + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + + MachException::Message::iterator pos; + MachException::Message::iterator begin = m_exception_messages.begin(); + MachException::Message::iterator end = m_exception_messages.end(); + for (pos = begin; pos != end; ++pos) + { + int resume_signal = -1; + ThreadSP thread_sp = m_thread_list.FindThreadByID(pos->state.thread_port); + if (thread_sp.get()) + resume_signal = thread_sp->GetResumeSignal(); + if (log) + log->Printf ("Replying to exception %d for thread 0x%4.4x (resume_signal = %i).", std::distance(begin, pos), thread_sp->GetID(), resume_signal); + Error curr_error (pos->Reply (Task().GetTaskPort(), GetID(), resume_signal)); + + // Only report the first error + if (curr_error.Fail() && error.Success()) + error = curr_error; + + error.LogIfError(log, "Error replying to exception"); + } + + // Erase all exception message as we should have used and replied + // to them all already. + m_exception_messages.clear(); + } + return error; +} + + +Error +ProcessMacOSX::PrivateResume (lldb::tid_t tid) +{ + + Mutex::Locker locker(m_exception_messages_mutex); + Error error (ReplyToAllExceptions()); + + // Let the thread prepare to resume and see if any threads want us to + // step over a breakpoint instruction (ProcessWillResume will modify + // the value of stepOverBreakInstruction). + //StateType process_state = m_thread_list.ProcessWillResume(this); + + // Set our state accordingly + SetPrivateState (eStateRunning); + + // Now resume our task. + error = Task().Resume(); + return error; +} + +// Called by the exception thread when an exception has been received from +// our process. The exception message is completely filled and the exception +// data has already been copied. +void +ProcessMacOSX::ExceptionMessageReceived (const MachException::Message& exceptionMessage) +{ + Mutex::Locker locker(m_exception_messages_mutex); + + if (m_exception_messages.empty()) + Task().Suspend(); + + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "ProcessMacOSX::ExceptionMessageReceived ( )"); + + // Use a locker to automatically unlock our mutex in case of exceptions + // Add the exception to our internal exception stack + m_exception_messages.push_back(exceptionMessage); +} + + +//bool +//ProcessMacOSX::GetProcessInfo (struct kinfo_proc* proc_info) +//{ +// int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, GetID() }; +// size_t buf_size = sizeof(struct kinfo_proc); +// +// if (::sysctl (mib, (unsigned)(sizeof(mib)/sizeof(int)), &proc_info, &buf_size, NULL, 0) == 0) +// return buf_size > 0; +// +// return false; +//} +// +// +void +ProcessMacOSX::ExceptionMessageBundleComplete() +{ + // We have a complete bundle of exceptions for our child process. + Mutex::Locker locker(m_exception_messages_mutex); + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); + if (!m_exception_messages.empty()) + { + SetPrivateState (eStateStopped); + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size()); + } +} + +bool +ProcessMacOSX::ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno ) +{ + if (stdin_fileno) + *stdin_fileno = m_child_stdin; + if (stdout_fileno) + *stdout_fileno = m_child_stdout; + if (stderr_fileno) + *stderr_fileno = m_child_stderr; + // Stop the stdio thread if we have one, but don't have it close the child + // file descriptors since we are giving control of these descriptors to the + // caller + bool close_child_fds = false; + StopSTDIOThread(close_child_fds); + return true; +} + +void +ProcessMacOSX::AppendSTDOUT (const char* s, size_t len) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s (<%d> %s) ...", __FUNCTION__, len, s); + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.append(s, len); + + // FIXME: Make a real data object for this and put it out. + BroadcastEventIfUnique (eBroadcastBitSTDOUT); +} + +lldb::pid_t +ProcessMacOSX::LaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + PDLaunchType launch_type, + Error &launch_err) +{ + // Clear out and clean up from any current state + Clear(); + + m_arch_spec = arch_spec; + + if (launch_type == eLaunchDefault) + launch_type = eLaunchPosixSpawn; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("%s( path = '%s', argv = %p, envp = %p, launch_type = %u )", __FUNCTION__, path, argv, envp, launch_type); + + // Fork a child process for debugging + SetPrivateState (eStateLaunching); + switch (launch_type) + { + case eLaunchForkExec: + SetID(ProcessMacOSX::ForkChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + + case eLaunchPosixSpawn: + SetID(ProcessMacOSX::PosixSpawnChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + +#if defined (__arm__) + + case eLaunchSpringBoard: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext != NULL) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, launch_err); + } + } + break; + +#endif + + default: + // Invalid launch + launch_err.SetErrorToGenericError (); + return LLDB_INVALID_PROCESS_ID; + } + + lldb::pid_t pid = GetID(); + + if (pid == LLDB_INVALID_PROCESS_ID) + { + // If we don't have a valid process ID and no one has set the error, + // then return a generic error + if (launch_err.Success()) + launch_err.SetErrorToGenericError (); + } + else + { + // Make sure we can get our task port before going any further + Task().GetTaskPortForProcessID (launch_err); + + // If that goes well then kick off our exception thread + if (launch_err.Success()) + Task().StartExceptionThread(launch_err); + + if (launch_err.Success()) + { + //m_path = path; +// size_t i; +// if (argv) +// { +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); +// } + + StartSTDIOThread(); + + if (launch_type == eLaunchPosixSpawn) + { + + //SetState (eStateAttaching); + errno = 0; + if (::ptrace (PT_ATTACHEXC, pid, 0, 0) == 0) + launch_err.Clear(); + else + launch_err.SetErrorToErrno(); + + if (launch_err.Fail() || log) + launch_err.PutToLog(log, "::ptrace (PT_ATTACHEXC, pid = %i, 0, 0 )", pid); + + if (launch_err.Success()) + m_flags.Set (eFlagsAttached); + else + SetPrivateState (eStateExited); + } + else + { + launch_err.Clear(); + } + } + else + { + // We were able to launch the process, but not get its task port + // so now we need to make it sleep with da fishes. + SetID(LLDB_INVALID_PROCESS_ID); + ::ptrace (PT_KILL, pid, 0, 0 ); + ::kill (pid, SIGCONT); + pid = LLDB_INVALID_PROCESS_ID; + } + + } + return pid; +} + +lldb::pid_t +ProcessMacOSX::PosixSpawnChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + Error &err +) +{ + posix_spawnattr_t attr; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + + Error local_err; // Errors that don't affect the spawning. + if (log) + log->Printf ("%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); + err.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnattr_init ( &attr )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + + err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + +#if !defined(__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu = arch_spec.GetCPUType(); + if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE) + { + size_t ocount = 0; + err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount); + + if (err.Fail() != 0 || ocount != 1) + return LLDB_INVALID_PROCESS_ID; + } + +#endif + + lldb_utility::PseudoTerminal pty; + + posix_spawn_file_actions_t file_actions; + err.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + int file_actions_valid = err.Success(); + if (!file_actions_valid || log) + err.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )"); + Error stdio_err; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (file_actions_valid) + { + // If the user specified any STDIO files, then use those + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stderr_path != NULL && stderr_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stderr_path, O_RDWR, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stderr_path); + } + + if (stdin_path != NULL && stdin_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdin_path, O_RDONLY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdin_path); + } + + if (stdout_path != NULL && stdout_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdout_path, O_WRONLY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdout_path); + } + } + else + { + // The user did not specify any STDIO files, use a pseudo terminal. + // Callers can then access the file handles using the + // ProcessMacOSX::ReleaseChildFileDescriptors() function, otherwise + // this class will spawn a thread that tracks STDIO and buffers it. + process->SetSTDIOIsOurs(true); + char error_str[1024]; + if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, error_str, sizeof(error_str))) + { + const char* slave_name = pty.GetSlaveName(error_str, sizeof(error_str)); + if (slave_name == NULL) + slave_name = "/dev/null"; + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, slave_name, O_RDWR|O_NOCTTY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR|O_NOCTTY, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, slave_name, O_RDONLY|O_NOCTTY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY|O_NOCTTY, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, slave_name, O_WRONLY|O_NOCTTY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY|O_NOCTTY, mode = 0 )", slave_name); + } + else + { + if (error_str[0]) + stdio_err.SetErrorString(error_str); + else + stdio_err.SetErrorString("Unable to open master side of pty for inferior."); + } + + } + err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); + + if (stdio_err.Success()) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + else + { + err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); + } + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (err.Fail()) + pid = LLDB_INVALID_PROCESS_ID; + + if (file_actions_valid) + { + local_err.SetError( ::posix_spawn_file_actions_destroy (&file_actions), eErrorTypePOSIX); + if (local_err.Fail() || log) + local_err.PutToLog(log, "::posix_spawn_file_actions_destroy ( &file_actions )"); + } + + return pid; +} + +lldb::pid_t +ProcessMacOSX::ForkChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + Error &launch_err +) +{ + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (stdin_path || stdout_path || stderr_path) + { + assert(!"TODO: ForkChildForPTraceDebugging doesn't currently support fork/exec with user file handles..."); + } + else + { + + // Use a fork that ties the child process's stdin/out/err to a pseudo + // terminal so we can read it in our ProcessMacOSX::STDIOThread + // as unbuffered io. + lldb_utility::PseudoTerminal pty; + char error_str[1024]; + pid = pty.Fork(error_str, sizeof(error_str)); + + if (pid < 0) + { + launch_err.SetErrorString (error_str); + //-------------------------------------------------------------- + // Error during fork. + //-------------------------------------------------------------- + return pid; + } + else if (pid == 0) + { + //-------------------------------------------------------------- + // Child process + //-------------------------------------------------------------- + ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process + ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions + + // If our parent is setgid, lets make sure we don't inherit those + // extra powers due to nepotism. + ::setgid (getgid ()); + + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (0, 0); // Set the child process group to match its pid + + // Sleep a bit to before the exec call + ::sleep (1); + + // Turn this process into + ::execv (path, (char * const *)argv); + // Exit with error code. Child process should have taken + // over in above exec call and if the exec fails it will + // exit the child process below. + ::exit (127); + } + else + { + //-------------------------------------------------------------- + // Parent process + //-------------------------------------------------------------- + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (pid, pid); // Set the child process group to match its pid + + if (process != NULL) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + return pid; +} + +#if defined (__arm__) + +lldb::pid_t +ProcessMacOSX::SBLaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + Error &launch_err +) +{ + // Clear out and clean up from any current state + Clear(); + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); + + // Fork a child process for debugging + SetState(eStateLaunching); + m_pid = ProcessMacOSX::SBLaunchForDebug(path, argv, envp, this, launch_err); + if (m_pid != 0) + { + m_flags |= eFlagsUsingSBS; + //m_path = path; +// size_t i; +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); + Task().StartExceptionThread(); + StartSTDIOThread(); + SetState (eStateAttaching); + int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0); + if (err == 0) + { + m_flags |= eFlagsAttached; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "successfully attached to pid %d", m_pid); + } + else + { + SetState (eStateExited); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "error: failed to attach to pid %d", m_pid); + } + } + return m_pid; +} + +#include +#include "CFBundle.h" +#include "CFData.h" +#include "CFString.h" + +lldb::pid_t +ProcessMacOSX::SBLaunchForDebug +( + const char *app_bundle_path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + Error &launch_err +) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); + CFAllocatorRef alloc = kCFAllocatorDefault; + if (argv[0] == NULL) + return LLDB_INVALID_PROCESS_ID; + + size_t argc = 0; + // Count the number of arguments + while (argv[argc] != NULL) + argc++; + + // Enumerate the arguments + size_t first_launch_arg_idx = 1; + CFReleaser launch_argv; + + if (argv[first_launch_arg_idx]) + { + size_t launch_argc = argc > 0 ? argc - 1 : 0; + launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); + size_t i; + char const *arg; + CFString launch_arg; + for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) + { + launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); + if (launch_arg.get() != NULL) + CFArrayAppendValue(launch_argv.get(), launch_arg.get()); + else + break; + } + } + + // Next fill in the arguments dictionary. Note, the envp array is of the form + // Variable=value but SpringBoard wants a CF dictionary. So we have to convert + // this here. + + CFReleaser launch_envp; + + if (envp[0]) + { + launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + const char *value; + int name_len; + CFString name_string, value_string; + + for (int i = 0; envp[i] != NULL; i++) + { + value = strstr (envp[i], "="); + + // If the name field is empty or there's no =, skip it. Somebody's messing with us. + if (value == NULL || value == envp[i]) + continue; + + name_len = value - envp[i]; + + // Now move value over the "=" + value++; + + name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); + value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); + CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); + } + } + + CFString stdout_cf_path; + CFString stderr_cf_path; + PseudoTerminal pty; + + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stdout_path) + stdout_cf_path.SetFileSystemRepresentation (stdout_path); + if (stderr_path) + stderr_cf_path.SetFileSystemRepresentation (stderr_path); + } + else + { + process->SetSTDIOIsOurs(true); + PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); + if (pty_err == PseudoTerminal::success) + { + const char* slave_name = pty.SlaveName(); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); + if (slave_name && slave_name[0]) + { + ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); + stdout_cf_path.SetFileSystemRepresentation (slave_name); + stderr_cf_path.(stdout_cf_path); + } + } + } + + if (stdout_cf_path.get() == NULL) + stdout_cf_path.SetFileSystemRepresentation ("/dev/null"); + if (stderr_cf_path.get() == NULL) + stderr_cf_path.SetFileSystemRepresentation ("/dev/null"); + + CFBundle bundle(app_bundle_path); + CFStringRef bundleIDCFStr = bundle.GetIdentifier(); + std::string bundleID; + if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + { + struct stat app_bundle_stat; + if (::stat (app_bundle_path, &app_bundle_stat) < 0) + { + launch_err.SetError(errno, eErrorTypePOSIX); + launch_err.SetErrorStringWithFormat("%s: \"%s\".\n", launch_err.AsString(), app_bundle_path); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: %s", __FUNCTION__, launch_err.AsCString()); + } + else + { + launch_err.SetError(-1, eErrorTypeGeneric); + launch_err.SetErrorStringWithFormat("Failed to extract CFBundleIdentifier from %s.\n", app_bundle_path); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); + } + return LLDB_INVALID_PROCESS_ID; + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); + + + CFData argv_data(NULL); + + if (launch_argv.get()) + { + if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); + return LLDB_INVALID_PROCESS_ID; + } + } + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); + + // Find SpringBoard + SBSApplicationLaunchError sbs_error = 0; + sbs_error = SBSLaunchApplication ( bundleIDCFStr, + (CFURLRef)NULL, // openURL + launch_argv.get(), + launch_envp.get(), // CFDictionaryRef environment + stdout_cf_path.get(), + stderr_cf_path.get(), + SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); + + + launch_err.SetError(sbs_error, eErrorTypeSpringBoard); + + if (sbs_error == SBSApplicationLaunchErrorSuccess) + { + static const useconds_t pid_poll_interval = 200000; + static const useconds_t pid_poll_timeout = 30000000; + + useconds_t pid_poll_total = 0; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired + // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started + // yet, or that it died very quickly (if you weren't using waitForDebugger). + while (!pid_found && pid_poll_total < pid_poll_timeout) + { + usleep (pid_poll_interval); + pid_poll_total += pid_poll_interval; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); + pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + } + + if (pid_found) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); + } + else + { + LogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); + } + return pid; + } + + LogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); + return LLDB_INVALID_PROCESS_ID; +} + +#endif // #if defined (__arm__) + + +#include "MachThreadContext_x86_64.h" +#include "MachThreadContext_i386.h" +#include "MachThreadContext_arm.h" + +void +ProcessMacOSX::Initialize() +{ + static bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + + MachThreadContext_x86_64::Initialize(); + MachThreadContext_i386::Initialize(); + MachThreadContext_arm::Initialize(); + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessMacOSXLog::DisableLog, + ProcessMacOSXLog::EnableLog, + ProcessMacOSXLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessMacOSX::GetPluginNameStatic(), log_callbacks); + + + } +} + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h new file mode 100644 index 000000000000..8388d4e46fec --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h @@ -0,0 +1,490 @@ +//===-- ProcessMacOSX.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MacOSXProcess_H_ +#define liblldb_MacOSXProcess_H_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +// Project includes +#include "MachTask.h" +#include "MachException.h" + +typedef enum PDLaunch +{ + eLaunchDefault = 0, + eLaunchPosixSpawn, + eLaunchForkExec, +#if defined (__arm__) + eLaunchSpringBoard, +#endif +} PDLaunchType; + + + +class ThreadMacOSX; +class MachThreadContext; + +class ProcessMacOSX : + public lldb_private::Process +{ +public: + friend class ThreadMacOSX; + friend class MachTask; + + typedef MachThreadContext* (*CreateArchCalback) (const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static Process* + CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessMacOSX(lldb_private::Target& target, lldb_private::Listener &listener); + + virtual + ~ProcessMacOSX(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path); // Can be NULL + + virtual void + DidLaunch (); + + virtual lldb_private::Error + WillAttach (lldb::pid_t pid); + + virtual lldb_private::Error + DoAttach (lldb::pid_t pid); + + virtual void + DidAttach (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (); + + virtual lldb_private::Error + WillDetach (); + + virtual lldb_private::Error + DoDetach (); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + virtual lldb::addr_t + GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t + GetSTDOUT (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + GetSTDERR (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpoint (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpoint (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual lldb_private::DynamicLoader * + GetDynamicLoader (); + + static void + AddArchCreateCallback(const lldb_private::ArchSpec& arch_spec, + ProcessMacOSX::CreateArchCalback callback); + +protected: + + bool m_stdio_ours; // True if we created and own the child STDIO file handles, false if they were supplied to us and owned by someone else + int m_child_stdin; + int m_child_stdout; + int m_child_stderr; + MachTask m_task; // The mach task for this process + lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) + lldb::thread_t m_stdio_thread; // Thread ID for the thread that watches for child process stdio + lldb_private::Mutex m_stdio_mutex; // Multithreaded protection for stdio + std::string m_stdout_data; + MachException::Message::collection m_exception_messages; // A collection of exception messages caught when listening to the exception port + lldb_private::Mutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages + lldb_private::ArchSpec m_arch_spec; + std::auto_ptr m_dynamic_loader_ap; +// lldb::thread_t m_wait_thread; + lldb::ByteOrder m_byte_order; + + //---------------------------------------------------------------------- + // Child process control + //---------------------------------------------------------------------- + lldb::pid_t + LaunchForDebug (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + PDLaunchType launch_type, + lldb_private::Error &launch_err); + + static lldb::pid_t + ForkChildForPTraceDebugging (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + lldb_private::Error &launch_err); + + static lldb::pid_t + PosixSpawnChildForPTraceDebugging (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + lldb_private::Error &launch_err); + +#if defined (__arm__) + lldb::pid_t + SBLaunchForDebug (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + lldb_private::Error &launch_err); + + static lldb::pid_t + SBLaunchForDebug (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + lldb_private::Error &launch_err); +#endif + + //---------------------------------------------------------------------- + // Exception thread functions + //---------------------------------------------------------------------- + bool + StartSTDIOThread (); + + void + StopSTDIOThread (bool close_child_fds); + + static void * + STDIOThread (void *arg); + + void + ExceptionMessageReceived (const MachException::Message& exceptionMessage); + + void + ExceptionMessageBundleComplete (); + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + ProcessIDIsValid ( ) const; + + MachTask& + Task() { return m_task; } + + const MachTask& + Task() const { return m_task; } + + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + void + SetChildFileDescriptors (int stdin_fileno, int stdout_fileno, int stderr_fileno) + { + m_child_stdin = stdin_fileno; + m_child_stdout = stdout_fileno; + m_child_stderr = stderr_fileno; + } + + int + GetStdinFileDescriptor () const + { + return m_child_stdin; + } + + int + GetStdoutFileDescriptor () const + { + return m_child_stdout; + } + int + GetStderrFileDescriptor () const + { + return m_child_stderr; + } + bool + ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno ); + + void + AppendSTDOUT (const char* s, size_t len); + + void + CloseChildFileDescriptors () + { + if (m_child_stdin >= 0) + { + ::close (m_child_stdin); + m_child_stdin = -1; + } + if (m_child_stdout >= 0) + { + ::close (m_child_stdout); + m_child_stdout = -1; + } + if (m_child_stderr >= 0) + { + ::close (m_child_stderr); + m_child_stderr = -1; + } + } + + bool + ProcessUsingSpringBoard() const + { + return m_flags.IsSet(eFlagsUsingSBS); + } + + lldb_private::ArchSpec& + GetArchSpec() + { + return m_arch_spec; + } + const lldb_private::ArchSpec& + GetArchSpec() const + { + return m_arch_spec; + } + + CreateArchCalback + GetArchCreateCallback(); + + enum + { + eFlagsNone = 0, + eFlagsAttached = (1 << 0), + eFlagsUsingSBS = (1 << 1) + }; + + void + Clear ( ); + + lldb_private::Error + ReplyToAllExceptions(); + + lldb_private::Error + PrivateResume ( lldb::tid_t tid); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + + bool + STDIOIsOurs() const + { + return m_stdio_ours; + } + + void + SetSTDIOIsOurs(bool b) + { + m_stdio_ours = b; + } + + uint32_t + UpdateThreadListIfNeeded (); + +private: + + void + DidLaunchOrAttach (); + + lldb_private::Error + DoSIGSTOP (bool clear_all_breakpoints); + + lldb_private::Error + WillLaunchOrAttach (); + +// static void * +// WaitForChildProcessToExit (void *pid_ptr); +// +// + //------------------------------------------------------------------ + // For ProcessMacOSX only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ProcessMacOSX); + +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp new file mode 100644 index 000000000000..4bfd1ff466e8 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp @@ -0,0 +1,124 @@ +//===-- ProcessMacOSXLog.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessMacOSXLog.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + + +static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. +Log * +ProcessMacOSXLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = g_log; + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessMacOSXLog::DisableLog () +{ + if (g_log) + { + delete g_log; + g_log = NULL; + } +} + +Log * +ProcessMacOSXLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm) +{ + DisableLog (); + g_log = new Log (log_stream_sp); + if (g_log) + { + uint32_t flag_bits = 0; + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; iPrintf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = PD_LOG_DEFAULT; + g_log->GetMask().SetAllFlagBits(flag_bits); + g_log->GetOptions().SetAllFlagBits(log_options); + } + return g_log; +} + +void +ProcessMacOSXLog::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + "\tall - turn on all available logging categories\n" + "\tbreak - log breakpoints\n" + "\tdefault - enable the default set of logging categories for liblldb\n" + "\tmemory - log memory reads and writes\n" + "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n" + "\tdata-long - log memory bytes for memory reads and writes for all transactions\n" + "\tprocess - log process events and activities\n" + "\tprotections - log memory protections\n" + "\ttask - log mach task calls\n" + "\tthread - log thread events and activities\n" + "\tstep - log step related activities\n" + "\tverbose - enable verbose loggging\n" + "\twatch - log watchpoint related activities\n", ProcessMacOSX::GetPluginNameStatic()); +} + + +void +ProcessMacOSXLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h new file mode 100644 index 000000000000..cb2a4e8ee982 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h @@ -0,0 +1,62 @@ +//===-- ProcessMacOSXLog.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessMacOSXLog_h_ +#define liblldb_ProcessMacOSXLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define PD_LOG_VERBOSE (1u << 0) +#define PD_LOG_PROCESS (1u << 1) +#define PD_LOG_THREAD (1u << 2) +#define PD_LOG_EXCEPTIONS (1u << 3) +#define PD_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define PD_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define PD_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define PD_LOG_MEMORY_PROTECTIONS (1u << 7) // Log memory protection changes +#define PD_LOG_BREAKPOINTS (1u << 8) +#define PD_LOG_WATCHPOINTS (1u << 9) +#define PD_LOG_STEP (1u << 10) +#define PD_LOG_TASK (1u << 11) +#define PD_LOG_ALL (UINT32_MAX) +#define PD_LOG_DEFAULT (PD_LOG_PROCESS |\ + PD_LOG_TASK |\ + PD_LOG_THREAD |\ + PD_LOG_EXCEPTIONS |\ + PD_LOG_MEMORY |\ + PD_LOG_MEMORY_DATA_SHORT |\ + PD_LOG_BREAKPOINTS |\ + PD_LOG_WATCHPOINTS |\ + PD_LOG_STEP ) + +class ProcessMacOSXLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessMacOSXLog_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp new file mode 100644 index 000000000000..835d003a9b24 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp @@ -0,0 +1,1819 @@ +//===-- ProcessMacOSXRemote.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// ProcessMacOSXRemote.cpp +// liblldb +// +// Created by Greg Clayton on 4/21/09. +// +// +//---------------------------------------------------------------------- + +// C Includes +#include + +// C++ Includes +//#include +//#include + +// Other libraries and framework includes + +// Project includes +#include "ProcessMacOSXRemote.h" +#include "ProcessMacOSXLog.h" +#include "ThreadMacOSX.h" + +Process* +ProcessMacOSXRemote::CreateInstance (Target &target) +{ + return new ProcessMacOSXRemote (target); +} + +bool +ProcessMacOSXRemote::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +//---------------------------------------------------------------------- +// ProcessMacOSXRemote constructor +//---------------------------------------------------------------------- +ProcessMacOSXRemote::ProcessMacOSXRemote(Target& target) : + Process (target), + m_flags (0), + m_arch_spec (), + m_dynamic_loader_ap (), + m_byte_order(eByteOrderInvalid) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessMacOSXRemote::~DCProcessMacOSXRemote() +{ + Clear(); +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +lldb::pid_t +ProcessMacOSXRemote::DoLaunch +( + Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ +// ::LogSetBitMask (PD_LOG_DEFAULT); +// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); +// ::LogSetLogFile ("/dev/stdout"); + + ObjectFile * object_file = module->GetObjectFile(); + if (object_file) + { + char exec_file_path[PATH_MAX]; + FileSpec* file_spec_ptr = object_file->GetFileSpec(); + if (file_spec_ptr) + file_spec_ptr->GetPath(exec_file_path, sizeof(exec_file_path)); + + ArchSpec arch_spec(module->GetArchitecture()); + + switch (arch_spec.GetCPUType()) + { + + } + // Set our user ID to our process ID. + SetID(LaunchForDebug(exec_file_path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, eLaunchDefault, GetError())); + } + else + { + // Set our user ID to an invalid process ID. + SetID(LLDB_INVALID_PROCESS_ID); + GetError().SetErrorToGenericError (); + GetError().SetErrorStringWithFormat ("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString()); + } + + // Return the process ID we have + return GetID(); +} + +lldb::pid_t +ProcessMacOSXRemote::DoAttach (lldb::pid_t attach_pid) +{ + // Set our user ID to the attached process ID (which can be invalid if + // the attach fails + lldb::pid_t pid = AttachForDebug(attach_pid); + SetID(pid); + +// if (pid != LLDB_INVALID_PROCESS_ID) +// { +// // Wait for a process stopped event, but don't consume it +// if (WaitForEvents(LLDB_EVENT_STOPPED, NULL, 30)) +// { +// } +// } +// + // Return the process ID we have + return pid; +} + + +void +ProcessMacOSXRemote::DidLaunch () +{ + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + m_dynamic_loader_ap.reset(); + } + else + { + Module * exe_module = GetTarget().GetExecutableModule ().get(); + assert(exe_module); + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + assert(exe_objfile); + m_byte_order = exe_objfile->GetByteOrder(); + assert(m_byte_order != eByteOrderInvalid); + // Install a signal handler so we can catch when our child process + // dies and set the exit status correctly. + m_wait_thread = Host::ThreadCreate (ProcessMacOSXRemote::WaitForChildProcessToExit, &m_uid, &m_error); + if (m_wait_thread != LLDB_INVALID_HOST_THREAD) + { + // Don't need to get the return value of this thread, so just let + // it clean up after itself when it dies. + Host::ThreadDetach (m_wait_thread, NULL); + } + m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "macosx-dyld")); + } + +} + +void +ProcessMacOSXRemote::DidAttach () +{ + DidLaunch (); + m_need_to_run_did_attach = true; +} + +bool +ProcessMacOSXRemote::DoResume () +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Resume()"); + State state = GetState(); + + if (CanResume(state)) + { + PrivateResume(LLDB_INVALID_THREAD_ID); + } + else if (state == eStateRunning) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort()); + GetError().Clear(); + + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort()); + GetError().SetError(UINT_MAX, Error::Generic); + } + + return GetError().Success(); +} + +size_t +ProcessMacOSXRemote::GetSoftwareBreakpointTrapOpcode (BreakpointSite *bp_site) +{ + ModuleSP exe_module_sp(GetTarget().GetExecutableModule()); + if (exe_module_sp.get()) + { + const ArchSpec &exe_arch = exe_module_sp->GetArchitecture(); + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + + switch (exe_arch.GetCPUType()) + { + case CPU_TYPE_ARM: + // TODO: fill this in for ARM. We need to dig up the symbol for + // the address in the breakpoint locaiton and figure out if it is + // an ARM or Thumb breakpoint. + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + break; + + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + break; + + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + break; + + default: + assert(!"Unhandled architecture in ProcessMacOSXRemote::GetSoftwareBreakpointTrapOpcode()"); + return 0; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_loc->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + } + // No executable yet, so we can't tell what the breakpoint opcode will be. + return 0; +} +uint32_t +ProcessMacOSXRemote::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ProcessMacOSXRemote::%s (pid = %4.4x)", __FUNCTION__, GetID()); + + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize() == 0 || stop_id != m_thread_list.GetID()) + { + m_thread_list.SetID (stop_id); + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + task_t task = Task().TaskPort(); + Error err(::task_threads (task, &thread_list, &thread_list_count), Error::MachKernel); + + if (log || err.Fail()) + err.Log(log, "::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); + + if (err.GetError() == KERN_SUCCESS && thread_list_count > 0) + { + ThreadList curr_thread_list; + + size_t idx; + // Iterator through the current thread list and see which threads + // we already have in our list (keep them), which ones we don't + // (add them), and which ones are not around anymore (remove them). + for (idx = 0; idx < thread_list_count; ++idx) + { + const lldb::tid_t tid = thread_list[idx]; + ThreadSP thread_sp(m_thread_list.FindThreadByID (tid)); + if (thread_sp.get() == NULL) + thread_sp.reset (new ThreadMacOSX (this, tid)); + curr_thread_list.AddThread(thread_sp); + } + + m_thread_list = curr_thread_list; + + // Free the vm memory given to us by ::task_threads() + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (lldb::tid_t)); + ::vm_deallocate (::mach_task_self(), + (vm_address_t)thread_list, + thread_list_size); + } + } + return m_thread_list.GetSize(); +} + +bool +ProcessMacOSXRemote::ShouldStop () +{ + // If we are attaching, let our dynamic loader plug-in know so it can get + // an initial list of shared libraries. + if (m_need_to_run_did_attach && m_dynamic_loader_ap.get()) + { + m_need_to_run_did_attach = false; + m_dynamic_loader_ap->DidAttach(); + } + + // We must be attaching if we don't already have a valid architecture + if (!m_arch_spec.IsValid()) + { + Module *exe_module = GetTarget().GetExecutableModule().get(); + if (exe_module) + m_arch_spec = exe_module->GetArchitecture(); + } + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + UpdateThreadListIfNeeded (); + + if (m_thread_list.ShouldStop()) + { + // Let each thread know of any exceptions + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + task_t task = m_task.TaskPort(); + size_t i; + for (i=0; iNotifyException (m_exception_messages[i].state); + } + } + if (log) + m_exception_messages[i].Log(log); + } + return true; + } + return false; +} + +bool +ProcessMacOSXRemote::DoHalt () +{ + return Kill (SIGINT); +} + +bool +ProcessMacOSXRemote::WillDetach () +{ + State state = GetState(); + + if (IsRunning(state)) + { + m_error.SetErrorToGenericError(); + m_error.SetErrorString("Process must be stopped in order to detach."); + return false; + } + return true; +} + +bool +ProcessMacOSXRemote::DoDetach () +{ + m_use_public_queue = false; + bool success = Detach(); + m_use_public_queue = true; + if (success) + SetState (eStateDetached); + return success; +} + +bool +ProcessMacOSXRemote::DoKill (int signal) +{ + return Kill (signal); +} + + +//------------------------------------------------------------------ +// Thread Queries +//------------------------------------------------------------------ + +Thread * +ProcessMacOSXRemote::GetCurrentThread () +{ + return m_thread_list.GetCurrentThread().get(); +} + +ByteOrder +ProcessMacOSXRemote::GetByteOrder () const +{ + return m_byte_order; +} + + + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessMacOSXRemote::IsAlive () +{ + return MachTask::IsValid (Task().TaskPort()); +} + +bool +ProcessMacOSXRemote::IsRunning () +{ + return LLDB_STATE_IS_RUNNING(GetState()); +} + +lldb::addr_t +ProcessMacOSXRemote::GetImageInfoAddress() +{ + return Task().GetDYLDAllImageInfosAddress(); +} + +DynamicLoader * +ProcessMacOSXRemote::GetDynamicLoader() +{ + return m_dynamic_loader_ap.get(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ + +size_t +ProcessMacOSXRemote::DoReadMemory (lldb::addr_t addr, void *buf, size_t size) +{ + return Task().ReadMemory(addr, buf, size); +} + +size_t +ProcessMacOSXRemote::DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size) +{ + return Task().WriteMemory(addr, buf, size); +} + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ + +size_t +ProcessMacOSXRemote::GetSTDOUT (char *buf, size_t buf_size) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + Mutex::Locker locker(m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + } + } + return bytes_available; +} + +size_t +ProcessMacOSXRemote::GetSTDERR (char *buf, size_t buf_size) +{ + return 0; +} + +bool +ProcessMacOSXRemote::EnableBreakpoint (BreakpointLocation *bp) +{ + assert (bp != NULL); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + lldb::user_id_t breakID = bp->GetID(); + lldb::addr_t addr = bp->GetAddress(); + if (bp->IsEnabled()) + { + if (log) + log->Printf("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) breakpoint already enabled.", breakID); + return true; + } + else + { + if (bp->HardwarePreferred()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp->GetThreadID()).get(); + if (thread) + { + bp->SetHardwareIndex (thread->EnableHardwareBreakpoint(bp)); + if (bp->IsHardware()) + { + bp->SetEnabled(true); + return true; + } + } + } + + const size_t break_op_size = GetSoftwareBreakpointTrapOpcode (bp); + assert (break_op_size > 0); + const uint8_t * const break_op = bp->GetTrapOpcodeBytes(); + + if (break_op_size > 0) + { + // Save the original opcode by reading it + if (m_task.ReadMemory(addr, bp->GetSavedOpcodeBytes(), break_op_size) == break_op_size) + { + // Write a software breakpoint in place of the original opcode + if (m_task.WriteMemory(addr, break_op, break_op_size) == break_op_size) + { + uint8_t verify_break_op[4]; + if (m_task.ReadMemory(addr, verify_break_op, break_op_size) == break_op_size) + { + if (memcmp(break_op, verify_break_op, break_op_size) == 0) + { + bp->SetEnabled(true); + if (log) + log->Printf("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) SUCCESS.", breakID, (uint64_t)addr); + return true; + } + else + { + GetError().SetErrorString("Failed to verify the breakpoint trap in memory."); + } + } + else + { + GetError().SetErrorString("Unable to read memory to verify breakpoint trap."); + } + } + else + { + GetError().SetErrorString("Unable to write breakpoint trap to memory."); + } + } + else + { + GetError().SetErrorString("Unable to read memory at breakpoint address."); + } + } + } + + if (log) + { + const char *err_string = GetError().AsCString(); + log->Printf ("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) error: %s", + breakID, err_string ? err_string : "NULL"); + } + GetError().SetErrorToGenericError(); + return false; +} + +bool +ProcessMacOSXRemote::DisableBreakpoint (BreakpointLocation *bp) +{ + assert (bp != NULL); + lldb::addr_t addr = bp->GetAddress(); + lldb::user_id_t breakID = bp->GetID(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) addr = 0x%8.8llx", breakID, (uint64_t)addr); + + if (bp->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp->GetThreadID()).get(); + if (thread) + { + if (thread->DisableHardwareBreakpoint(bp)) + { + bp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) (hardware) => success", breakID); + return true; + } + } + return false; + } + + const size_t break_op_size = bp->GetByteSize(); + assert (break_op_size > 0); + const uint8_t * const break_op = bp->GetTrapOpcodeBytes(); + if (break_op_size > 0) + { + // Clear a software breakoint instruction + uint8_t curr_break_op[break_op_size]; + bool break_op_found = false; + + // Read the breakpoint opcode + if (m_task.ReadMemory(addr, curr_break_op, break_op_size) == break_op_size) + { + bool verify = false; + if (bp->IsEnabled()) + { + // Make sure we have the a breakpoint opcode exists at this address + if (memcmp(curr_break_op, break_op, break_op_size) == 0) + { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (m_task.WriteMemory(addr, bp->GetSavedOpcodeBytes(), break_op_size) == break_op_size) + { + verify = true; + } + else + { + GetError().SetErrorString("Memory write failed when restoring original opcode."); + } + } + else + { + GetError().SetErrorString("Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has already been restored + verify = true; + } + } + else + { + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) is already disabled", breakID); + // Set verify to true and so we can check if the original opcode is there + verify = true; + } + + if (verify) + { + uint8_t verify_opcode[break_op_size]; + // Verify that our original opcode made it back to the inferior + if (m_task.ReadMemory(addr, verify_opcode, break_op_size) == break_op_size) + { + // compare the memory we just read with the original opcode + if (memcmp(bp->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0) + { + // SUCCESS + bp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) SUCCESS", breakID); + return true; + } + else + { + if (break_op_found) + GetError().SetErrorString("Failed to restore original opcode."); + } + } + else + { + GetError().SetErrorString("Failed to read memory to verify that breakpoint trap was restored."); + } + } + } + else + { + GetError().SetErrorString("Unable to read memory that should contain the breakpoint trap."); + } + } + + GetError().SetErrorToGenericError(); + return false; +} + +bool +ProcessMacOSXRemote::EnableWatchpoint (WatchpointLocation *wp) +{ + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + lldb::addr_t addr = wp->GetAddress(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + if (log) + log->Printf ("ProcessMacOSXRemote::EnableWatchpoint(watchID = %d)", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessMacOSXRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return true; + } + else + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + wp->SetHardwareIndex (thread->EnableHardwareWatchpoint (wp)); + if (wp->IsHardware ()) + { + wp->SetEnabled(true); + return true; + } + } + else + { + GetError().SetErrorString("Watchpoints currently only support thread specific watchpoints."); + } + } + } + return false; +} + +bool +ProcessMacOSXRemote::DisableWatchpoint (WatchpointLocation *wp) +{ + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + + lldb::addr_t addr = wp->GetAddress(); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr); + + if (wp->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + if (thread->DisableHardwareWatchpoint (wp)) + { + wp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSXRemote::Disablewatchpoint (watchID = %d) addr = 0x%8.8llx (hardware) => success", watchID, (uint64_t)addr); + return true; + } + } + } + // TODO: clear software watchpoints if we implement them + } + else + { + GetError().SetErrorString("Watchpoint location argument was NULL."); + } + GetError().SetErrorToGenericError(); + return false; +} + + +static ProcessMacOSXRemote::CreateArchCalback +ArchDCScriptInterpreter::TypeMap(const ArchSpec& arch_spec, ProcessMacOSXRemote::CreateArchCalback callback, bool add ) +{ + // We must wrap the "g_arch_map" file static in a function to avoid + // any global constructors so we don't get a build verification error + typedef std::multimap ArchToProtocolMap; + static ArchToProtocolMap g_arch_map; + + if (add) + { + g_arch_map.insert(std::make_pair(arch_spec, callback)); + return callback; + } + else + { + ArchToProtocolMap::const_iterator pos = g_arch_map.find(arch_spec); + if (pos != g_arch_map.end()) + { + return pos->second; + } + } + return NULL; +} + +void +ProcessMacOSXRemote::AddArchCreateDCScriptInterpreter::Type(const ArchSpec& arch_spec, CreateArchCalback callback) +{ + ArchDCScriptInterpreter::TypeMap (arch_spec, callback, true); +} + +ProcessMacOSXRemote::CreateArchCalback +ProcessMacOSXRemote::GetArchCreateDCScriptInterpreter::Type() +{ + return ArchDCScriptInterpreter::TypeMap (m_arch_spec, NULL, false); +} + +void +ProcessMacOSXRemote::Clear() +{ + // Clear any cached thread list while the pid and task are still valid + + m_task.Clear(); + // Now clear out all member variables + CloseChildFileDescriptors(); + + m_flags = eFlagsNone; + m_thread_list.Clear(); + { + Mutex::Locker locker(m_exception_messages_mutex); + m_exception_messages.clear(); + } + +} + + +bool +ProcessMacOSXRemote::Kill (int signal) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSXRemote::Kill(signal = %d)", signal); + State state = GetState(); + + if (IsRunning(state)) + { + if (::kill (GetID(), signal) == 0) + { + GetError().Clear(); + } + else + { + GetError().SetErrorToErrno(); + GetError().LogIfError(log, "ProcessMacOSXRemote::Kill(%d)", signal); + } + } + else + { + if (log) + log->Printf ("ProcessMacOSXRemote::Kill(signal = %d) pid %u (task = 0x%4.4x) was't running, ignoring...", signal, GetID(), m_task.TaskPort()); + GetError().Clear(); + } + return GetError().Success(); + +} + + +bool +ProcessMacOSXRemote::Detach() +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Detach()"); + + State state = GetState(); + + if (!IsRunning(state)) + { + // Resume our process + PrivateResume(LLDB_INVALID_THREAD_ID); + + // We have resumed and now we wait for that event to get posted + Event event; + if (WaitForPrivateEvents(LLDB_EVENT_RUNNING, &event, 2) == false) + return false; + + + // We need to be stopped in order to be able to detach, so we need + // to send ourselves a SIGSTOP + if (Kill(SIGSTOP)) + { + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + + lldb::pid_t pid = GetID(); + // Wait for our process stop event to get posted + if (WaitForPrivateEvents(LLDB_EVENT_STOPPED, &event, 2) == false) + { + GetError().Log(log, "::kill (pid = %u, SIGSTOP)", pid); + return false; + } + + // Shut down the exception thread and cleanup our exception remappings + m_task.ShutDownExceptionThread(); + + // Detach from our process while we are stopped. + errno = 0; + + // Detach from our process + ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + + GetError().SetErrorToErrno(); + + if (log || GetError().Fail()) + GetError().Log(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + + // Resume our task + m_task.Resume(); + + // NULL our task out as we have already retored all exception ports + m_task.Clear(); + + // Clear out any notion of the process we once were + Clear(); + } + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Detach() error: process must be stopped (SIGINT the process first)."); + } + return false; +} + + + +void +ProcessMacOSXRemote::ReplyToAllExceptions() +{ + Mutex::Locker locker(m_exception_messages_mutex); + if (m_exception_messages.empty() == false) + { + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + + MachException::Message::iterator pos; + MachException::Message::iterator begin = m_exception_messages.begin(); + MachException::Message::iterator end = m_exception_messages.end(); + for (pos = begin; pos != end; ++pos) + { + if (log) + log->Printf ("Replying to exception %d...", std::distance(begin, pos)); + int resume_signal = 0; + ThreadSP thread_sp = m_thread_list.FindThreadByID(pos->state.thread_port); + if (thread_sp.get()) + resume_signal = thread_sp->GetResumeSignal(); + GetError() = pos->Reply (Task().TaskPort(), GetID(), resume_signal); + GetError().LogIfError(log, "Error replying to exception"); + } + + // Erase all exception message as we should have used and replied + // to them all already. + m_exception_messages.clear(); + } +} +void +ProcessMacOSXRemote::PrivateResume (lldb::tid_t tid) +{ + Mutex::Locker locker(m_exception_messages_mutex); + ReplyToAllExceptions(); + + // Let the thread prepare to resume and see if any threads want us to + // step over a breakpoint instruction (ProcessWillResume will modify + // the value of stepOverBreakInstruction). + //StateType process_state = m_thread_list.ProcessWillResume(this); + + // Set our state accordingly + SetState(eStateRunning); + + // Now resume our task. + GetError() = m_task.Resume(); + +} + +// Called by the exception thread when an exception has been received from +// our process. The exception message is completely filled and the exception +// data has already been copied. +void +ProcessMacOSXRemote::ExceptionMessageReceived (const MachException::Message& exceptionMessage) +{ + Mutex::Locker locker(m_exception_messages_mutex); + + if (m_exception_messages.empty()) + m_task.Suspend(); + + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "ProcessMacOSXRemote::ExceptionMessageReceived ( )"); + + // Use a locker to automatically unlock our mutex in case of exceptions + // Add the exception to our internal exception stack + m_exception_messages.push_back(exceptionMessage); +} + + +//bool +//ProcessMacOSXRemote::GetProcessInfo (struct kinfo_proc* proc_info) +//{ +// int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, GetID() }; +// size_t buf_size = sizeof(struct kinfo_proc); +// +// if (::sysctl (mib, (unsigned)(sizeof(mib)/sizeof(int)), &proc_info, &buf_size, NULL, 0) == 0) +// return buf_size > 0; +// +// return false; +//} +// +// +void +ProcessMacOSXRemote::ExceptionMessageBundleComplete() +{ + // We have a complete bundle of exceptions for our child process. + Mutex::Locker locker(m_exception_messages_mutex); + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); + if (!m_exception_messages.empty()) + { + SetState (eStateStopped); + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size()); + } +} + +bool +ProcessMacOSXRemote::ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno ) +{ + if (stdin_fileno) + *stdin_fileno = m_child_stdin; + if (stdout_fileno) + *stdout_fileno = m_child_stdout; + if (stderr_fileno) + *stderr_fileno = m_child_stderr; + // Stop the stdio thread if we have one, but don't have it close the child + // file descriptors since we are giving control of these descriptors to the + // caller + bool close_child_fds = false; + StopSTDIOThread(close_child_fds); + return true; +} + +void +ProcessMacOSXRemote::AppendSTDOUT (char* s, size_t len) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::%s (<%d> %s) ...", __FUNCTION__, len, s); + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.append(s, len); + AppendEvent (LLDB_EVENT_STDIO); +} + +void * +ProcessMacOSXRemote::STDIOThread(void *arg) +{ + ProcessMacOSXRemote *proc = (ProcessMacOSXRemote*) arg; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSXRemote::%s (arg = %p) thread starting...", __FUNCTION__, arg); + + // We start use a base and more options so we can control if we + // are currently using a timeout on the mach_msg. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main thread loop + // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + Error err; + int stdout_fd = proc->GetStdoutFileDescriptor(); + int stderr_fd = proc->GetStderrFileDescriptor(); + if (stdout_fd == stderr_fd) + stderr_fd = -1; + + while (stdout_fd >= 0 || stderr_fd >= 0) + { + ::pthread_testcancel (); + + fd_set read_fds; + FD_ZERO (&read_fds); + if (stdout_fd >= 0) + FD_SET (stdout_fd, &read_fds); + if (stderr_fd >= 0) + FD_SET (stderr_fd, &read_fds); + int nfds = std::max(stdout_fd, stderr_fd) + 1; + + int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); + if (log) + log->Printf("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + + if (num_set_fds < 0) + { + int select_errno = errno; + if (log) + { + err.SetError (select_errno, Error::POSIX); + err.LogIfError(log, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + } + + switch (select_errno) + { + case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO + break; + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return NULL; + break; + case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + break; + } + } + else if (num_set_fds == 0) + { + } + else + { + char s[1024]; + s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination + int bytes_read = 0; + if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) + { + do + { + bytes_read = ::read (stdout_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); + stdout_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + + if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) + { + do + { + bytes_read = ::read (stderr_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); + stderr_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + } + } + + if (log) + log->Printf("ProcessMacOSXRemote::%s (%p): thread exiting...", __FUNCTION__, arg); + + return NULL; +} + +lldb::pid_t +ProcessMacOSXRemote::AttachForDebug (lldb::pid_t pid) +{ + // Clear out and clean up from any current state + Clear(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (pid != 0) + { + SetState(eStateAttaching); + SetID(pid); + // Let ourselves know we are going to be using SBS if the correct flag bit is set... +#if defined (__arm__) + if (IsSBProcess(pid)) + m_flags |= eFlagsUsingSBS; +#endif + m_task.StartExceptionThread(GetError()); + + if (GetError().Success()) + { + if (ptrace (PT_ATTACHEXC, pid, 0, 0) == 0) + { + m_flags.Set (eFlagsAttached); + // Sleep a bit to let the exception get received and set our process status + // to stopped. + ::usleep(250000); + if (log) + log->Printf ("successfully attached to pid %d", pid); + return GetID(); + } + else + { + GetError().SetErrorToErrno(); + if (log) + log->Printf ("error: failed to attach to pid %d", pid); + } + } + else + { + GetError().Log(log, "ProcessMacOSXRemote::%s (pid = %i) failed to start exception thread", __FUNCTION__, pid); + } + } + return LLDB_INVALID_PROCESS_ID; +} + +lldb::pid_t +ProcessMacOSXRemote::LaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + PDLaunchType launch_type, + Error &launch_err) +{ + // Clear out and clean up from any current state + Clear(); + + m_arch_spec = arch_spec; + + if (launch_type == eLaunchDefault) + launch_type = eLaunchPosixSpawn; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("%s( path = '%s', argv = %p, envp = %p, launch_type = %u )", __FUNCTION__, path, argv, envp, launch_type); + + // Fork a child process for debugging + SetState(eStateLaunching); + switch (launch_type) + { + case eLaunchForkExec: + SetID(ProcessMacOSXRemote::ForkChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + + case eLaunchPosixSpawn: + SetID(ProcessMacOSXRemote::PosixSpawnChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + +#if defined (__arm__) + + case eLaunchSpringBoard: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext != NULL) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, launch_err); + } + } + break; + +#endif + + default: + // Invalid launch + launch_err.SetErrorToGenericError (); + return LLDB_INVALID_PROCESS_ID; + } + + lldb::pid_t pid = GetID(); + + if (pid == LLDB_INVALID_PROCESS_ID) + { + // If we don't have a valid process ID and no one has set the error, + // then return a generic error + if (launch_err.Success()) + launch_err.SetErrorToGenericError (); + } + else + { + // Make sure we can get our task port before going any further + m_task.TaskPortForProcessID (launch_err); + + // If that goes well then kick off our exception thread + if (launch_err.Success()) + m_task.StartExceptionThread(launch_err); + + if (launch_err.Success()) + { + //m_path = path; +// size_t i; +// if (argv) +// { +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); +// } + + StartSTDIOThread(); + + if (launch_type == eLaunchPosixSpawn) + { + + //SetState (eStateAttaching); + errno = 0; + if (::ptrace (PT_ATTACHEXC, pid, 0, 0) == 0) + launch_err.Clear(); + else + launch_err.SetErrorToErrno(); + + if (launch_err.Fail() || log) + launch_err.Log(log, "::ptrace (PT_ATTACHEXC, pid = %i, 0, 0 )", pid); + + if (launch_err.Success()) + m_flags.Set (eFlagsAttached); + else + SetState (eStateExited); + } + else + { + launch_err.Clear(); + } + } + else + { + // We were able to launch the process, but not get its task port + // so now we need to make it sleep with da fishes. + SetID(LLDB_INVALID_PROCESS_ID); + ::kill (pid, SIGCONT); + ::kill (pid, SIGKILL); + pid = LLDB_INVALID_PROCESS_ID; + } + + } + return pid; +} + +lldb::pid_t +ProcessMacOSXRemote::PosixSpawnChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSXRemote* process, + Error &err +) +{ + posix_spawnattr_t attr; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + + Error local_err; // Errors that don't affect the spawning. + if (log) + log->Printf ("%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); + err.SetError( ::posix_spawnattr_init (&attr), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnattr_init ( &attr )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + + err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + +#if !defined(__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu = arch_spec.GetCPUType(); + if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE) + { + size_t ocount = 0; + err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount); + + if (err.Fail() != 0 || ocount != 1) + return LLDB_INVALID_PROCESS_ID; + } + +#endif + + PseudoTerminal pty; + + posix_spawn_file_actions_t file_actions; + err.SetError( ::posix_spawn_file_actions_init (&file_actions), Error::POSIX); + int file_actions_valid = err.Success(); + if (!file_actions_valid || log) + err.Log(log, "::posix_spawn_file_actions_init ( &file_actions )"); + Error stdio_err; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (file_actions_valid) + { + // If the user specified any STDIO files, then use those + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stderr_path != NULL && stderr_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stderr_path, O_RDWR, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stderr_path); + } + + if (stdin_path != NULL && stdin_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdin_path, O_RDONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdin_path); + } + + if (stdout_path != NULL && stdout_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdout_path, O_WRONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdout_path); + } + } + else + { + // The user did not specify any STDIO files, use a pseudo terminal. + // Callers can then access the file handles using the + // ProcessMacOSXRemote::ReleaseChildFileDescriptors() function, otherwise + // this class will spawn a thread that tracks STDIO and buffers it. + process->SetSTDIOIsOurs(true); + if (pty.OpenFirstAvailableMaster(O_RDWR, &stdio_err)) + { + const char* slave_name = pty.GetSlaveName(&stdio_err); + if (slave_name == NULL) + slave_name = "/dev/null"; + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, slave_name, O_RDWR, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, slave_name, O_RDONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, slave_name, O_WRONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", slave_name); + } + } + err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); + + if (stdio_err.Success()) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + else + { + err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); + } + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (err.Fail()) + pid = LLDB_INVALID_PROCESS_ID; + + if (file_actions_valid) + { + local_err.SetError( ::posix_spawn_file_actions_destroy (&file_actions), Error::POSIX); + if (local_err.Fail() || log) + local_err.Log(log, "::posix_spawn_file_actions_destroy ( &file_actions )"); + } + + return pid; +} + +lldb::pid_t +ProcessMacOSXRemote::ForkChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSXRemote* process, + Error &launch_err +) +{ + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (stdin_path || stdout_path || stderr_path) + { + assert(!"TODO: ForkChildForPTraceDebugging doesn't currently support fork/exec with user file handles..."); + } + else + { + + // Use a fork that ties the child process's stdin/out/err to a pseudo + // terminal so we can read it in our ProcessMacOSXRemote::STDIOThread + // as unbuffered io. + PseudoTerminal pty; + pid = pty.Fork(&launch_err); + + if (pid < 0) + { + //-------------------------------------------------------------- + // Error during fork. + //-------------------------------------------------------------- + return pid; + } + else if (pid == 0) + { + //-------------------------------------------------------------- + // Child process + //-------------------------------------------------------------- + ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process + ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions + + // If our parent is setgid, lets make sure we don't inherit those + // extra powers due to nepotism. + ::setgid (getgid ()); + + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (0, 0); // Set the child process group to match its pid + + // Sleep a bit to before the exec call + ::sleep (1); + + // Turn this process into + ::execv (path, (char * const *)argv); + // Exit with error code. Child process should have taken + // over in above exec call and if the exec fails it will + // exit the child process below. + ::exit (127); + } + else + { + //-------------------------------------------------------------- + // Parent process + //-------------------------------------------------------------- + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (pid, pid); // Set the child process group to match its pid + + if (process != NULL) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + return pid; +} + +#if defined (__arm__) + +lldb::pid_t +ProcessMacOSXRemote::SBLaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + Error &launch_err +) +{ + // Clear out and clean up from any current state + Clear(); + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); + + // Fork a child process for debugging + SetState(eStateLaunching); + m_pid = ProcessMacOSXRemote::SBLaunchForDebug(path, argv, envp, this, launch_err); + if (m_pid != 0) + { + m_flags |= eFlagsUsingSBS; + //m_path = path; +// size_t i; +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); + m_task.StartExceptionThread(); + StartSTDIOThread(); + SetState (eStateAttaching); + int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0); + if (err == 0) + { + m_flags |= eFlagsAttached; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "successfully attached to pid %d", m_pid); + } + else + { + SetState (eStateExited); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "error: failed to attach to pid %d", m_pid); + } + } + return m_pid; +} + +#include +#include "CFBundle.h" +#include "CFData.h" +#include "CFString.h" + +lldb::pid_t +ProcessMacOSXRemote::SBLaunchForDebug +( + const char *app_bundle_path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSXRemote* process, + Error &launch_err +) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); + CFAllocatorRef alloc = kCFAllocatorDefault; + if (argv[0] == NULL) + return LLDB_INVALID_PROCESS_ID; + + size_t argc = 0; + // Count the number of arguments + while (argv[argc] != NULL) + argc++; + + // Enumerate the arguments + size_t first_launch_arg_idx = 1; + CFReleaser launch_argv; + + if (argv[first_launch_arg_idx]) + { + size_t launch_argc = argc > 0 ? argc - 1 : 0; + launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); + size_t i; + char const *arg; + CFString launch_arg; + for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) + { + launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); + if (launch_arg.get() != NULL) + CFArrayAppendValue(launch_argv.get(), launch_arg.get()); + else + break; + } + } + + // Next fill in the arguments dictionary. Note, the envp array is of the form + // Variable=value but SpringBoard wants a CF dictionary. So we have to convert + // this here. + + CFReleaser launch_envp; + + if (envp[0]) + { + launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + const char *value; + int name_len; + CFString name_string, value_string; + + for (int i = 0; envp[i] != NULL; i++) + { + value = strstr (envp[i], "="); + + // If the name field is empty or there's no =, skip it. Somebody's messing with us. + if (value == NULL || value == envp[i]) + continue; + + name_len = value - envp[i]; + + // Now move value over the "=" + value++; + + name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); + value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); + CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); + } + } + + CFString stdout_cf_path; + CFString stderr_cf_path; + PseudoTerminal pty; + + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stdout_path) + stdout_cf_path.SetFileSystemRepresentation (stdout_path); + if (stderr_path) + stderr_cf_path.SetFileSystemRepresentation (stderr_path); + } + else + { + process->SetSTDIOIsOurs(true); + PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR); + if (pty_err == PseudoTerminal::success) + { + const char* slave_name = pty.SlaveName(); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); + if (slave_name && slave_name[0]) + { + ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); + stdout_cf_path.SetFileSystemRepresentation (slave_name); + stderr_cf_path.(stdout_cf_path); + } + } + } + + if (stdout_cf_path.get() == NULL) + stdout_cf_path.SetFileSystemRepresentation ("/dev/null"); + if (stderr_cf_path.get() == NULL) + stderr_cf_path.SetFileSystemRepresentation ("/dev/null"); + + CFBundle bundle(app_bundle_path); + CFStringRef bundleIDCFStr = bundle.GetIdentifier(); + std::string bundleID; + if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + { + struct stat app_bundle_stat; + if (::stat (app_bundle_path, &app_bundle_stat) < 0) + { + launch_err.SetError(errno, Error::POSIX); + launch_err.SetErrorStringWithFormat ("%s: \"%s\".\n", launch_err.AsString(), app_bundle_path); + } + else + { + launch_err.SetError(-1, Error::Generic); + launch_err.SetErrorStringWithFormat ("Failed to extract CFBundleIdentifier from %s.\n", app_bundle_path); + } + return LLDB_INVALID_PROCESS_ID; + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); + + + CFData argv_data(NULL); + + if (launch_argv.get()) + { + if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); + return LLDB_INVALID_PROCESS_ID; + } + } + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); + + // Find SpringBoard + SBSApplicationLaunchError sbs_error = 0; + sbs_error = SBSLaunchApplication ( bundleIDCFStr, + (CFURLRef)NULL, // openURL + launch_argv.get(), + launch_envp.get(), // CFDictionaryRef environment + stdout_cf_path.get(), + stderr_cf_path.get(), + SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); + + + launch_err.SetError(sbs_error, Error::SpringBoard); + + if (sbs_error == SBSApplicationLaunchErrorSuccess) + { + static const useconds_t pid_poll_interval = 200000; + static const useconds_t pid_poll_timeout = 30000000; + + useconds_t pid_poll_total = 0; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired + // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started + // yet, or that it died very quickly (if you weren't using waitForDebugger). + while (!pid_found && pid_poll_total < pid_poll_timeout) + { + usleep (pid_poll_interval); + pid_poll_total += pid_poll_interval; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); + pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + } + + if (pid_found) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); + } + else + { + LogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); + } + return pid; + } + + LogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); + return LLDB_INVALID_PROCESS_ID; +} + +#endif // #if defined (__arm__) + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h new file mode 100644 index 000000000000..01905c6192a2 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h @@ -0,0 +1,206 @@ +//===-- ProcessMacOSXRemote.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// ProcessMacOSXRemote.h +// liblldb +// +// Created by Greg Clayton on 4/21/09. +// +// +//---------------------------------------------------------------------- + +#ifndef liblldb_ProcessMacOSXRemote_H_ +#define liblldb_ProcessMacOSXRemote_H_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +class ThreadMacOSXRemote; + +class ProcessMacOSXRemote : + public Process +{ +public: + friend class ThreadMacOSX; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessMacOSXRemote(Target& target); + virtual ~DCProcessMacOSXRemote(); + + static Process* CreateInstance (Target& target); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool CanDebug(Target &target); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb::pid_t DoLaunch (Module* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path); // Can be NULL + virtual void DidLaunch (); + virtual lldb::pid_t DoAttach (lldb::pid_t pid); + virtual void DidAttach (); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ +// virtual bool WillResume (); + virtual bool DoResume (); +// virtual void DidResume (); + + virtual bool DoHalt (); + virtual bool WillDetach (); + virtual bool DoDetach (); + virtual bool DoKill (int signal); + + virtual bool ShouldStop (); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool IsAlive (); + virtual bool IsRunning (); + virtual lldb::addr_t GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t DoReadMemory (lldb::addr_t addr, void *buf, size_t size); + virtual size_t DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t GetSTDOUT (char *buf, size_t buf_size); + virtual size_t GetSTDERR (char *buf, size_t buf_size); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual bool + EnableBreakpoint (lldb::BreakpointSite *bp_site); + + virtual bool + DisableBreakpoint (lldb::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual bool EnableWatchpoint (WatchpointLocation *wp_loc); + virtual bool DisableWatchpoint (WatchpointLocation *wp_loc); + + //------------------------------------------------------------------ + // Thread Queries + //------------------------------------------------------------------ + virtual Thread * GetCurrentThread (); + virtual bool SetCurrentThread (lldb::tid_t tid); + virtual Thread * GetThreadAtIndex (uint32_t idx); + virtual Thread * GetThreadByID (lldb::tid_t tid); + virtual size_t GetNumThreads (); + + virtual ByteOrder GetByteOrder () const; + + virtual DynamicLoader * + GetDynamicLoader (); + +protected: + Flags m_flags; // Process specific flags (see eFlags enums) + ArchSpec m_arch_spec; + std::auto_ptr m_dynamic_loader_ap; + ByteOrder m_byte_order; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + ProcessIDIsValid ( ) const; + + bool + IsRunning ( State state ) + { + return state == eStateRunning || IsStepping(state); + } + + bool + IsStepping ( State state) + { + return state == eStateStepping; + } + bool + CanResume ( State state) + { + return state == eStateStopped; + } + + ArchSpec& + GetArchSpec() + { + return m_arch_spec; + } + const ArchSpec& + GetArchSpec() const + { + return m_arch_spec; + } + + enum + { + eFlagsNone = 0, + eFlagsAttached = (1 << 0), + eFlagsUsingSBS = (1 << 1) + }; + + void + Clear ( ); + + Flags & + GetFlags () + { + return m_flags; + } + + const Flags & + GetFlags () const + { + return m_flags; + } + + uint32_t + UpdateThreadListIfNeeded (); + +private: + //------------------------------------------------------------------ + // For ProcessMacOSXRemote only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ProcessMacOSXRemote); + +}; + +#endif // liblldb_ProcessMacOSXRemote_H_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp new file mode 100644 index 000000000000..37472547f558 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp @@ -0,0 +1,1448 @@ +//===-- RegisterContextMach_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 "RegisterContextMach_arm.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" + +// Project includes +#include "ARM_GCC_Registers.h" +#include "ARM_DWARF_Registers.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_r0 = 0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, gpr_sp = gpr_r13, + gpr_r14, gpr_lr = gpr_r14, + gpr_r15, gpr_pc = gpr_r15, + gpr_cpsr, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, + + exc_exception, + exc_fsr, + exc_far, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + + +RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread, StackFrame *frame) : + RegisterContext(thread, frame), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; ireg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, dbg_##reg##i, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM } +#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU) + sizeof (RegisterContextMach_arm::EXC)) +// General purpose registers +static lldb::RegisterInfo +g_register_infos[] = +{ +// NAME ALT SZ OFFSET ENCODING FORMAT NATIVE COMPILER DWARF GENERIC +// ====== ======= == ============= ============= ============ ========== =============== =============== ========= +{ "r0", NULL, 4, GPR_OFFSET(0), eEncodingUint, eFormatHex, gpr_r0, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM }}, +{ "r1", NULL, 4, GPR_OFFSET(1), eEncodingUint, eFormatHex, gpr_r1, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM }}, +{ "r2", NULL, 4, GPR_OFFSET(2), eEncodingUint, eFormatHex, gpr_r2, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM }}, +{ "r3", NULL, 4, GPR_OFFSET(3), eEncodingUint, eFormatHex, gpr_r3, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM }}, +{ "r4", NULL, 4, GPR_OFFSET(4), eEncodingUint, eFormatHex, gpr_r4, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM }}, +{ "r5", NULL, 4, GPR_OFFSET(5), eEncodingUint, eFormatHex, gpr_r5, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM }}, +{ "r6", NULL, 4, GPR_OFFSET(6), eEncodingUint, eFormatHex, gpr_r6, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM }}, +{ "r7", NULL, 4, GPR_OFFSET(7), eEncodingUint, eFormatHex, gpr_r7, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP }}, +{ "r8", NULL, 4, GPR_OFFSET(8), eEncodingUint, eFormatHex, gpr_r8, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM }}, +{ "r9", NULL, 4, GPR_OFFSET(9), eEncodingUint, eFormatHex, gpr_r9, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM }}, +{ "r10", NULL, 4, GPR_OFFSET(10), eEncodingUint, eFormatHex, gpr_r10, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM }}, +{ "r11", NULL, 4, GPR_OFFSET(11), eEncodingUint, eFormatHex, gpr_r11, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM }}, +{ "r12", NULL, 4, GPR_OFFSET(12), eEncodingUint, eFormatHex, gpr_r12, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM }}, +{ "sp", "r13", 4, GPR_OFFSET(13), eEncodingUint, eFormatHex, gpr_sp, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP }}, +{ "lr", "r14", 4, GPR_OFFSET(14), eEncodingUint, eFormatHex, gpr_lr, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA }}, +{ "pc", "r15", 4, GPR_OFFSET(15), eEncodingUint, eFormatHex, gpr_pc, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC }}, +{ "cpsr", "psr", 4, GPR_OFFSET(16), eEncodingUint, eFormatHex, gpr_cpsr, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS }}, + +{ "s0", NULL, 4, FPU_OFFSET(0), eEncodingIEEE754,eFormatFloat, fpu_s0, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM }}, +{ "s1", NULL, 4, FPU_OFFSET(1), eEncodingIEEE754,eFormatFloat, fpu_s1, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM }}, +{ "s2", NULL, 4, FPU_OFFSET(2), eEncodingIEEE754,eFormatFloat, fpu_s2, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM }}, +{ "s3", NULL, 4, FPU_OFFSET(3), eEncodingIEEE754,eFormatFloat, fpu_s3, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM }}, +{ "s4", NULL, 4, FPU_OFFSET(4), eEncodingIEEE754,eFormatFloat, fpu_s4, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM }}, +{ "s5", NULL, 4, FPU_OFFSET(5), eEncodingIEEE754,eFormatFloat, fpu_s5, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM }}, +{ "s6", NULL, 4, FPU_OFFSET(6), eEncodingIEEE754,eFormatFloat, fpu_s6, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM }}, +{ "s7", NULL, 4, FPU_OFFSET(7), eEncodingIEEE754,eFormatFloat, fpu_s7, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM }}, +{ "s8", NULL, 4, FPU_OFFSET(8), eEncodingIEEE754,eFormatFloat, fpu_s8, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM }}, +{ "s9", NULL, 4, FPU_OFFSET(9), eEncodingIEEE754,eFormatFloat, fpu_s9, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM }}, +{ "s10", NULL, 4, FPU_OFFSET(10), eEncodingIEEE754,eFormatFloat, fpu_s10, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM }}, +{ "s11", NULL, 4, FPU_OFFSET(11), eEncodingIEEE754,eFormatFloat, fpu_s11, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM }}, +{ "s12", NULL, 4, FPU_OFFSET(12), eEncodingIEEE754,eFormatFloat, fpu_s12, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM }}, +{ "s13", NULL, 4, FPU_OFFSET(13), eEncodingIEEE754,eFormatFloat, fpu_s13, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM }}, +{ "s14", NULL, 4, FPU_OFFSET(14), eEncodingIEEE754,eFormatFloat, fpu_s14, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM }}, +{ "s15", NULL, 4, FPU_OFFSET(15), eEncodingIEEE754,eFormatFloat, fpu_s15, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM }}, +{ "s16", NULL, 4, FPU_OFFSET(16), eEncodingIEEE754,eFormatFloat, fpu_s16, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM }}, +{ "s17", NULL, 4, FPU_OFFSET(17), eEncodingIEEE754,eFormatFloat, fpu_s17, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM }}, +{ "s18", NULL, 4, FPU_OFFSET(18), eEncodingIEEE754,eFormatFloat, fpu_s18, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM }}, +{ "s19", NULL, 4, FPU_OFFSET(19), eEncodingIEEE754,eFormatFloat, fpu_s19, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM }}, +{ "s20", NULL, 4, FPU_OFFSET(20), eEncodingIEEE754,eFormatFloat, fpu_s20, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM }}, +{ "s21", NULL, 4, FPU_OFFSET(21), eEncodingIEEE754,eFormatFloat, fpu_s21, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM }}, +{ "s22", NULL, 4, FPU_OFFSET(22), eEncodingIEEE754,eFormatFloat, fpu_s22, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM }}, +{ "s23", NULL, 4, FPU_OFFSET(23), eEncodingIEEE754,eFormatFloat, fpu_s23, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM }}, +{ "s24", NULL, 4, FPU_OFFSET(24), eEncodingIEEE754,eFormatFloat, fpu_s24, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM }}, +{ "s25", NULL, 4, FPU_OFFSET(25), eEncodingIEEE754,eFormatFloat, fpu_s25, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM }}, +{ "s26", NULL, 4, FPU_OFFSET(26), eEncodingIEEE754,eFormatFloat, fpu_s26, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM }}, +{ "s27", NULL, 4, FPU_OFFSET(27), eEncodingIEEE754,eFormatFloat, fpu_s27, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM }}, +{ "s28", NULL, 4, FPU_OFFSET(28), eEncodingIEEE754,eFormatFloat, fpu_s28, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM }}, +{ "s29", NULL, 4, FPU_OFFSET(29), eEncodingIEEE754,eFormatFloat, fpu_s29, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM }}, +{ "s30", NULL, 4, FPU_OFFSET(30), eEncodingIEEE754,eFormatFloat, fpu_s30, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM }}, +{ "s31", NULL, 4, FPU_OFFSET(31), eEncodingIEEE754,eFormatFloat, fpu_s31, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM }}, +{ "fpscr", NULL, 4, FPU_OFFSET(32), eEncodingUint, eFormatHex, fpu_fpscr, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, + +{ "exception",NULL, 4, EXC_OFFSET(0), eEncodingUint, eFormatHex, exc_exception,{ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, +{ "fsr", NULL, 4, EXC_OFFSET(1), eEncodingUint, eFormatHex, exc_fsr, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, +{ "far", NULL, 4, EXC_OFFSET(2), eEncodingUint, eFormatHex, exc_far, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, + +{ DEFINE_DBG (bvr, 0) }, +{ DEFINE_DBG (bvr, 0) }, +{ DEFINE_DBG (bvr, 1) }, +{ DEFINE_DBG (bvr, 2) }, +{ DEFINE_DBG (bvr, 3) }, +{ DEFINE_DBG (bvr, 4) }, +{ DEFINE_DBG (bvr, 5) }, +{ DEFINE_DBG (bvr, 6) }, +{ DEFINE_DBG (bvr, 7) }, +{ DEFINE_DBG (bvr, 8) }, +{ DEFINE_DBG (bvr, 9) }, +{ DEFINE_DBG (bvr, 10) }, +{ DEFINE_DBG (bvr, 11) }, +{ DEFINE_DBG (bvr, 12) }, +{ DEFINE_DBG (bvr, 13) }, +{ DEFINE_DBG (bvr, 14) }, +{ DEFINE_DBG (bvr, 15) }, + +{ DEFINE_DBG (bcr, 0) }, +{ DEFINE_DBG (bcr, 0) }, +{ DEFINE_DBG (bcr, 1) }, +{ DEFINE_DBG (bcr, 2) }, +{ DEFINE_DBG (bcr, 3) }, +{ DEFINE_DBG (bcr, 4) }, +{ DEFINE_DBG (bcr, 5) }, +{ DEFINE_DBG (bcr, 6) }, +{ DEFINE_DBG (bcr, 7) }, +{ DEFINE_DBG (bcr, 8) }, +{ DEFINE_DBG (bcr, 9) }, +{ DEFINE_DBG (bcr, 10) }, +{ DEFINE_DBG (bcr, 11) }, +{ DEFINE_DBG (bcr, 12) }, +{ DEFINE_DBG (bcr, 13) }, +{ DEFINE_DBG (bcr, 14) }, +{ DEFINE_DBG (bcr, 15) }, + +{ DEFINE_DBG (wvr, 0) }, +{ DEFINE_DBG (wvr, 0) }, +{ DEFINE_DBG (wvr, 1) }, +{ DEFINE_DBG (wvr, 2) }, +{ DEFINE_DBG (wvr, 3) }, +{ DEFINE_DBG (wvr, 4) }, +{ DEFINE_DBG (wvr, 5) }, +{ DEFINE_DBG (wvr, 6) }, +{ DEFINE_DBG (wvr, 7) }, +{ DEFINE_DBG (wvr, 8) }, +{ DEFINE_DBG (wvr, 9) }, +{ DEFINE_DBG (wvr, 10) }, +{ DEFINE_DBG (wvr, 11) }, +{ DEFINE_DBG (wvr, 12) }, +{ DEFINE_DBG (wvr, 13) }, +{ DEFINE_DBG (wvr, 14) }, +{ DEFINE_DBG (wvr, 15) }, + +{ DEFINE_DBG (wcr, 0) }, +{ DEFINE_DBG (wcr, 0) }, +{ DEFINE_DBG (wcr, 1) }, +{ DEFINE_DBG (wcr, 2) }, +{ DEFINE_DBG (wcr, 3) }, +{ DEFINE_DBG (wcr, 4) }, +{ DEFINE_DBG (wcr, 5) }, +{ DEFINE_DBG (wcr, 6) }, +{ DEFINE_DBG (wcr, 7) }, +{ DEFINE_DBG (wcr, 8) }, +{ DEFINE_DBG (wcr, 9) }, +{ DEFINE_DBG (wcr, 10) }, +{ DEFINE_DBG (wcr, 11) }, +{ DEFINE_DBG (wcr, 12) }, +{ DEFINE_DBG (wcr, 13) }, +{ DEFINE_DBG (wcr, 14) }, +{ DEFINE_DBG (wcr, 15) } +}; + +// General purpose registers +static uint32_t +g_gpr_regnums[] = +{ + gpr_r0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_sp, + gpr_lr, + gpr_pc, + gpr_cpsr +}; + +// Floating point registers +static uint32_t +g_fpu_regnums[] = +{ + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, +}; + +// Exception registers + +static uint32_t +g_exc_regnums[] = +{ + exc_exception, + exc_fsr, + exc_far, +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + +void +RegisterContextMach_arm::Invalidate () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextMach_arm::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextMach_arm::GetRegisterInfoAtIndex (uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + +size_t +RegisterContextMach_arm::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextMach_arm::GetRegisterInfos () +{ + return g_register_infos; +} + + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextMach_arm::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextMach_arm::GetRegisterSet (uint32_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit i386. +//---------------------------------------------------------------------- +int +RegisterContextMach_arm::GetSetForNativeRegNum (int reg) +{ + if (reg < fpu_s0) + return GPRRegSet; + else if (reg < exc_exception) + return FPURegSet; + else if (reg < k_num_registers) + return EXCRegSet; + return -1; +} + +kern_return_t +RegisterContextMach_arm::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = GPRWordCount; + SetError(GPRRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count)); + } + return GetError(GPRRegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = FPUWordCount; + SetError(FPURegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count)); + } + return GetError(FPURegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = EXCWordCount; + SetError(EXCRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count)); + } + return GetError(EXCRegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::ReadDBG (bool force) +{ + int set = DBGRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = DBGWordCount; + SetError(DBGRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&dbg, &count)); + } + return GetError(DBGRegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(GPRRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount)); + return GetError(GPRRegSet, Write); +} + +kern_return_t +RegisterContextMach_arm::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(FPURegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount)); + return GetError(FPURegSet, Write); +} + +kern_return_t +RegisterContextMach_arm::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(EXCRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount)); + return GetError(EXCRegSet, Write); +} + +kern_return_t +RegisterContextMach_arm::WriteDBG () +{ + int set = DBGRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(DBGRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&dbg, DBGWordCount)); + return GetError(DBGRegSet, Write); +} + + +kern_return_t +RegisterContextMach_arm::ReadRegisterSet (uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR(force); + case FPURegSet: return ReadFPU(force); + case EXCRegSet: return ReadEXC(force); + case DBGRegSet: return ReadDBG(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +RegisterContextMach_arm::WriteRegisterSet (uint32_t set) +{ + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) + { + switch (set) + { + case GPRRegSet: return WriteGPR(); + case FPURegSet: return WriteFPU(); + case EXCRegSet: return WriteEXC(); + case DBGRegSet: return WriteDBG(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +void +RegisterContextMach_arm::LogDBGRegisters (Log *log, const DBG& dbg) +{ + if (log) + { + for (uint32_t i=0; i<16; i++) + log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }", + i, i, dbg.bvr[i], dbg.bcr[i], + i, i, dbg.wvr[i], dbg.wcr[i]); + } +} + + +bool +RegisterContextMach_arm::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + int set = RegisterContextMach_arm::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + value = gpr.r[reg - gpr_r0]; + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + value = fpu.floats.s[reg]; + break; + + case fpu_fpscr: + value = fpu.fpscr; + break; + + case exc_exception: + value = exc.exception; + break; + case exc_fsr: + value = exc.fsr; + break; + case exc_far: + value = exc.far; + break; + + default: + return false; + + } + return true; +} + + +bool +RegisterContextMach_arm::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.r[reg - gpr_r0] = value.UInt(0); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + fpu.floats.s[reg] = value.UInt(0); + break; + + case fpu_fpscr: + fpu.fpscr = value.UInt(0); + break; + + case exc_exception: + exc.exception = value.UInt(0); + break; + case exc_fsr: + exc.fsr = value.UInt(0); + break; + case exc_far: + exc.far = value.UInt(0); + break; + + default: + return false; + + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_arm::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + int set = RegisterContextMach_arm::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + data.SetData(&gpr.r[reg - gpr_r0], reg_info->byte_size, eByteOrderHost); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + data.SetData(&fpu.floats.s[reg - fpu_s0], reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fpscr: + data.SetData(&fpu.fpscr, reg_info->byte_size, eByteOrderHost); + break; + + case exc_exception: + data.SetData(&exc.exception, reg_info->byte_size, eByteOrderHost); + break; + + case exc_fsr: + data.SetData(&exc.fsr, reg_info->byte_size, eByteOrderHost); + break; + + case exc_far: + data.SetData(&exc.far, reg_info->byte_size, eByteOrderHost); + break; + + default: + return false; + } + return true; +} + +bool +RegisterContextMach_arm::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size)) + return false; + + uint32_t offset = data_offset; + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.r[reg - gpr_r0] = data.GetU32 (&offset); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + fpu.floats.s[reg - fpu_s0] = data.GetU32 (&offset); + break; + + case fpu_fpscr: + fpu.fpscr = data.GetU32 (&offset); + break; + + case exc_exception: + fpu.fpscr = data.GetU32 (&offset); + break; + + case exc_fsr: + exc.fsr = data.GetU32 (&offset); + break; + + case exc_far: + exc.far = data.GetU32 (&offset); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextMach_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t +RegisterContextMach_arm::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_pc; + case LLDB_REGNUM_GENERIC_SP: return gpr_sp; + case LLDB_REGNUM_GENERIC_FP: return gpr_r7; + case LLDB_REGNUM_GENERIC_RA: return gpr_lr; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_cpsr; + default: + break; + } + } + else if (kind == eRegisterKindDWARF) + { + switch (reg) + { + case dwarf_r0: return gpr_r0; + case dwarf_r1: return gpr_r1; + case dwarf_r2: return gpr_r2; + case dwarf_r3: return gpr_r3; + case dwarf_r4: return gpr_r4; + case dwarf_r5: return gpr_r5; + case dwarf_r6: return gpr_r6; + case dwarf_r7: return gpr_r7; + case dwarf_r8: return gpr_r8; + case dwarf_r9: return gpr_r9; + case dwarf_r10: return gpr_r10; + case dwarf_r11: return gpr_r11; + case dwarf_r12: return gpr_r12; + case dwarf_sp: return gpr_sp; + case dwarf_lr: return gpr_lr; + case dwarf_pc: return gpr_pc; + case dwarf_spsr: return gpr_cpsr; + + case dwarf_s0: return fpu_s0; + case dwarf_s1: return fpu_s1; + case dwarf_s2: return fpu_s2; + case dwarf_s3: return fpu_s3; + case dwarf_s4: return fpu_s4; + case dwarf_s5: return fpu_s5; + case dwarf_s6: return fpu_s6; + case dwarf_s7: return fpu_s7; + case dwarf_s8: return fpu_s8; + case dwarf_s9: return fpu_s9; + case dwarf_s10: return fpu_s10; + case dwarf_s11: return fpu_s11; + case dwarf_s12: return fpu_s12; + case dwarf_s13: return fpu_s13; + case dwarf_s14: return fpu_s14; + case dwarf_s15: return fpu_s15; + case dwarf_s16: return fpu_s16; + case dwarf_s17: return fpu_s17; + case dwarf_s18: return fpu_s18; + case dwarf_s19: return fpu_s19; + case dwarf_s20: return fpu_s20; + case dwarf_s21: return fpu_s21; + case dwarf_s22: return fpu_s22; + case dwarf_s23: return fpu_s23; + case dwarf_s24: return fpu_s24; + case dwarf_s25: return fpu_s25; + case dwarf_s26: return fpu_s26; + case dwarf_s27: return fpu_s27; + case dwarf_s28: return fpu_s28; + case dwarf_s29: return fpu_s29; + case dwarf_s30: return fpu_s30; + case dwarf_s31: return fpu_s31; + + default: + break; + } + } + else if (kind == eRegisterKindGCC) + { + switch (reg) + { + case gcc_r0: return gpr_r0; + case gcc_r1: return gpr_r1; + case gcc_r2: return gpr_r2; + case gcc_r3: return gpr_r3; + case gcc_r4: return gpr_r4; + case gcc_r5: return gpr_r5; + case gcc_r6: return gpr_r6; + case gcc_r7: return gpr_r7; + case gcc_r8: return gpr_r8; + case gcc_r9: return gpr_r9; + case gcc_r10: return gpr_r10; + case gcc_r11: return gpr_r11; + case gcc_r12: return gpr_r12; + case gcc_sp: return gpr_sp; + case gcc_lr: return gpr_lr; + case gcc_pc: return gpr_pc; + case gcc_cpsr: return gpr_cpsr; + } + } + return LLDB_INVALID_REGNUM; +} + + +uint32_t +RegisterContextMach_arm::NumSupportedHardwareBreakpoints () +{ +#if defined (__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many breakpoints are supported dynamically... + static uint32_t g_num_supported_hw_breakpoints = UINT_MAX + if (g_num_supported_hw_breakpoints == UINT_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_breakpoints = 0; + + // Read the DBGDIDR to get the number of available hardware breakpoints + // However, in some of our current armv7 processors, hardware + // breakpoints/watchpoints were not properly connected. So detect those + // cases using a field in a sysctl. For now we are using "hw.cpusubtype" + // field to distinguish CPU architectures. This is a hack until we can + // get fixed, at which point we will switch to + // using a different sysctl string that will tell us how many BRPs + // are available to us directly without having to read DBGDIDR. + uint32_t register_DBGDIDR; + + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numBRPs = bits(register_DBGDIDR, 27, 24); + // Zero is reserved for the BRP count, so don't increment it if it is zero + if (numBRPs > 0) + numBRPs++; + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, numBRPs); + + if (numBRPs > 0) + { + uint32_t cpu_subtype; + size_t len; + len = sizeof(cpusubtype); + // TODO: remove this hack and change to using hw.optional.xx when implmented + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype); + if (cpusubtype == CPU_SUBTYPE_ARM_V7) + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "Hardware breakpoints disabled for armv7 (rdar://problem/6372672)"); + else + g_num_supported_hw_breakpoints = numBRPs; + } + } + + } + return g_num_supported_hw_breakpoints; +#else + // TODO: figure out remote case here! + return 6; +#endif +} + +uint32_t +RegisterContextMach_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + // Make sure our address isn't bogus + if (addr & 1) + return LLDB_INVALID_INDEX32; + + kern_return_t kret = ReadDBG (false); + + if (kret == KERN_SUCCESS) + { + const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints(); + uint32_t i; + for (i=0; i 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size); + } + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextMach_arm::ClearHardwareBreakpoint (uint32_t hw_index) +{ + kern_return_t kret = ReadDBG (false); + + const uint32_t num_hw_points = NumSupportedHardwareBreakpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + dbg.bcr[hw_index] = 0; + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x", + hw_index, + hw_index, + dbg.bvr[hw_index], + hw_index, + dbg.bcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +uint32_t +RegisterContextMach_arm::NumSupportedHardwareWatchpoints () +{ +#if defined (__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many watchpoints are supported dynamically... + static uint32_t g_num_supported_hw_watchpoints = UINT_MAX; + if (g_num_supported_hw_watchpoints == UINT_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_watchpoints = 0; + // Read the DBGDIDR to get the number of available hardware breakpoints + // However, in some of our current armv7 processors, hardware + // breakpoints/watchpoints were not properly connected. So detect those + // cases using a field in a sysctl. For now we are using "hw.cpusubtype" + // field to distinguish CPU architectures. This is a hack until we can + // get fixed, at which point we will switch to + // using a different sysctl string that will tell us how many WRPs + // are available to us directly without having to read DBGDIDR. + + uint32_t register_DBGDIDR; + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1; + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, numWRPs); + + if (numWRPs > 0) + { + uint32_t cpusubtype; + size_t len; + len = sizeof(cpusubtype); + // TODO: remove this hack and change to using hw.optional.xx when implmented + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype); + + if (cpusubtype == CPU_SUBTYPE_ARM_V7) + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "Hardware watchpoints disabled for armv7 (rdar://problem/6372672)"); + else + g_num_supported_hw_watchpoints = numWRPs; + } + } + + } + return g_num_supported_hw_watchpoints; +#else + // TODO: figure out remote case here! + return 2; +#endif +} + + +uint32_t +RegisterContextMach_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write) +{ + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint(addr = %8.8p, size = %u, read = %u, write = %u)", addr, size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can't watch zero bytes + if (size == 0) + return LLDB_INVALID_INDEX32; + + // We must watch for either read or write + if (read == false && write == false) + return LLDB_INVALID_INDEX32; + + // Can't watch more than 4 bytes per WVR/WCR pair + if (size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair. Since we have at most so we can only watch + // until the next 4 byte boundary and we need to make sure we can properly + // encode this. + uint32_t addr_word_offset = addr % 4; + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset); + + uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset; + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask); + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Read the debug state + kern_return_t kret = ReadDBG (false); + + if (kret == KERN_SUCCESS) + { + // Check to make sure we have the needed hardware support + uint32_t i = 0; + + for (i=0; i 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextMach_arm::ClearHardwareWatchpoint (uint32_t hw_index) +{ + kern_return_t kret = ReadDBG (false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + dbg.wcr[hw_index] = 0; + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x", + hw_index, + hw_index, + dbg.wvr[hw_index], + hw_index, + dbg.wcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h new file mode 100644 index 000000000000..37821cdd5366 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h @@ -0,0 +1,302 @@ +//===-- RegisterContextMach_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_RegisterContextMach_arm_h_ +#define liblldb_RegisterContextMach_arm_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +// BCR address match type +#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21)) +#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21)) +#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21)) +#define BCR_M_RESERVED ((uint32_t)(3u << 21)) + +// Link a BVR/BCR or WVR/WCR pair to another +#define E_ENABLE_LINKING ((uint32_t)(1u << 20)) + +// Byte Address Select +#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5)) +#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6)) +#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7)) +#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8)) +#define BAS_IMVA_0_1 ((uint32_t)(3u << 5)) +#define BAS_IMVA_2_3 ((uint32_t)(3u << 7)) +#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5)) + +// Break only in priveleged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +class RegisterContextMach_arm : public lldb_private::RegisterContext +{ +public: + + RegisterContextMach_arm(lldb_private::Thread &thread, lldb_private::StackFrame *frame); + + virtual + ~RegisterContextMach_arm(); + + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + 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); + + struct GPR + { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[16]; + } floats; + uint32_t fpscr; + }; + +// struct NeonReg +// { +// uint8_t bytes[16]; +// }; +// +// struct VFPv3 +// { +// union { +// uint32_t s[32]; +// uint64_t d[32]; +// NeonReg q[16]; +// } v3; +// uint32_t fpscr; +// }; + + struct EXC + { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG + { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + static void + LogDBGRegisters (lldb_private::Log *log, const DBG& dbg); + +protected: + + typedef enum + { + GPRRegSet = 1, + FPURegSet = 2, + EXCRegSet = 3, + DBGRegSet = 4, + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t), + DBGWordCount = sizeof(DBG)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + DBG dbg; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + kern_return_t dbg_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + kern_return_t + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + case DBGRegSet: return dbg_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, kern_return_t err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + case DBGRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == KERN_SUCCESS; + } + + kern_return_t + ReadGPR (bool force); + + kern_return_t + ReadFPU (bool force); + + kern_return_t + ReadEXC (bool force); + + kern_return_t + ReadDBG (bool force); + + kern_return_t + WriteGPR (); + + kern_return_t + WriteFPU (); + + kern_return_t + WriteEXC (); + + kern_return_t + WriteDBG (); + + kern_return_t + ReadRegisterSet (uint32_t set, bool force); + + kern_return_t + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb::RegisterInfo * + GetRegisterInfos (); +}; + +#endif // liblldb_RegisterContextMach_arm_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp new file mode 100644 index 000000000000..daa4f0d49166 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp @@ -0,0 +1,1202 @@ +//===-- RegisterContextMach_i386.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 +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" + +// Project includes +#include "RegisterContextMach_i386.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_eax = 0, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum +{ + 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 +}; + +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 +}; + +RegisterContextMach_i386::RegisterContextMach_i386 (Thread &thread, StackFrame *frame) : + RegisterContext(thread, frame), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; ireg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, gpr_##reg +#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextMach_i386::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex, fpu_##reg +#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextMach_i386::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, fpu_##reg##i, { LLDB_INVALID_REGNUM, dwarf_##reg##i, LLDB_INVALID_REGNUM, gdb_##reg##i } + +#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextMach_i386::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex, exc_##reg +#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_i386::GPR) + sizeof (RegisterContextMach_i386::FPU) + sizeof (RegisterContextMach_i386::EXC)) + +static RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC REG KIND NUM DWARF REG KIND NUM GENERIC REG KIND NUM GDB REG KIND NUM +// =============================== ======================= =================== ========================== ========================== + { DEFINE_GPR(eax , NULL) , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax }}, + { DEFINE_GPR(ebx , NULL) , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx }}, + { DEFINE_GPR(ecx , NULL) , { gcc_ecx , dwarf_ecx , LLDB_INVALID_REGNUM , gdb_ecx }}, + { DEFINE_GPR(edx , NULL) , { gcc_edx , dwarf_edx , LLDB_INVALID_REGNUM , gdb_edx }}, + { DEFINE_GPR(edi , NULL) , { gcc_edi , dwarf_edi , LLDB_INVALID_REGNUM , gdb_edi }}, + { DEFINE_GPR(esi , NULL) , { gcc_esi , dwarf_esi , LLDB_INVALID_REGNUM , gdb_esi }}, + { DEFINE_GPR(ebp , "fp") , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp }}, + { DEFINE_GPR(esp , "sp") , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp }}, + { DEFINE_GPR(ss , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss }}, + { DEFINE_GPR(eflags , "flags") , { gcc_eflags , dwarf_eflags , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags }}, + { DEFINE_GPR(eip , "pc") , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip }}, + { DEFINE_GPR(cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs }}, + { DEFINE_GPR(ds , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds }}, + { DEFINE_GPR(es , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es }}, + { DEFINE_GPR(fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs }}, + { DEFINE_GPR(gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs }}, + + { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fcw }}, + { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fsw }}, + { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftw }}, + { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop }}, + { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ip }}, + { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs }}, + { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_dp }}, + { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds }}, + { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr }}, + { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_FPU_VECT(stmm,0) }, + { DEFINE_FPU_VECT(stmm,1) }, + { DEFINE_FPU_VECT(stmm,2) }, + { DEFINE_FPU_VECT(stmm,3) }, + { DEFINE_FPU_VECT(stmm,4) }, + { DEFINE_FPU_VECT(stmm,5) }, + { DEFINE_FPU_VECT(stmm,6) }, + { DEFINE_FPU_VECT(stmm,7) }, + { DEFINE_FPU_VECT(xmm,0) }, + { DEFINE_FPU_VECT(xmm,1) }, + { DEFINE_FPU_VECT(xmm,2) }, + { DEFINE_FPU_VECT(xmm,3) }, + { DEFINE_FPU_VECT(xmm,4) }, + { DEFINE_FPU_VECT(xmm,5) }, + { DEFINE_FPU_VECT(xmm,6) }, + { DEFINE_FPU_VECT(xmm,7) }, + + { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }} +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + +void +RegisterContextMach_i386::Invalidate () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextMach_i386::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextMach_i386::GetRegisterInfoAtIndex (uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + +size_t +RegisterContextMach_i386::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextMach_i386::GetRegisterInfos () +{ + return g_register_infos; +} + + +// General purpose registers +static uint32_t +g_gpr_regnums[] = +{ + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs +}; + +// Floating point registers +static uint32_t +g_fpu_regnums[] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7 +}; + +// Exception registers + +static uint32_t +g_exc_regnums[] = +{ + exc_trapno, + exc_err, + exc_faultvaddr +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextMach_i386::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextMach_i386::GetRegisterSet (uint32_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit i386. +//---------------------------------------------------------------------- +int +RegisterContextMach_i386::GetSetForNativeRegNum (int reg_num) +{ + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + + +void +RegisterContextMach_i386::LogGPR(Log *log, const char *title) +{ + if (log) + { + if (title) + log->Printf ("%s", title); + for (uint32_t i=0; iPrintf("%12s = 0x%8.8x", g_register_infos[reg].name, (&gpr.eax)[reg]); + } + } +} + + + +kern_return_t +RegisterContextMach_i386::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = GPRWordCount; + SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count)); + LogGPR (ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD), "RegisterContextMach_i386::ReadGPR()"); + } + return GetError(set, Read); +} + +kern_return_t +RegisterContextMach_i386::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = FPUWordCount; + SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count)); + } + return GetError(set, Read); +} + +kern_return_t +RegisterContextMach_i386::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = EXCWordCount; + SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count)); + } + return GetError(set, Read); +} + +kern_return_t +RegisterContextMach_i386::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +kern_return_t +RegisterContextMach_i386::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +kern_return_t +RegisterContextMach_i386::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +kern_return_t +RegisterContextMach_i386::ReadRegisterSet (uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR(force); + case FPURegSet: return ReadFPU(force); + case EXCRegSet: return ReadEXC(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +RegisterContextMach_i386::WriteRegisterSet (uint32_t set) +{ + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) + { + switch (set) + { + case GPRRegSet: return WriteGPR(); + case FPURegSet: return WriteFPU(); + case EXCRegSet: return WriteEXC(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +bool +RegisterContextMach_i386::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + int set = RegisterContextMach_i386::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + value = (&gpr.eax)[reg - gpr_eax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these + // registers + //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16); + return false; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + + +bool +RegisterContextMach_i386::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + (&gpr.eax)[reg - gpr_eax] = value.UInt(0); + break; + + case fpu_fcw: + fpu.fcw = value.UInt(0); + break; + + case fpu_fsw: + fpu.fsw = value.UInt(0); + break; + + case fpu_ftw: + fpu.ftw = value.UInt(0); + break; + + case fpu_fop: + fpu.fop = value.UInt(0); + break; + + case fpu_ip: + fpu.ip = value.UInt(0); + break; + + case fpu_cs: + fpu.cs = value.UInt(0); + break; + + case fpu_dp: + fpu.dp = value.UInt(0); + break; + + case fpu_ds: + fpu.ds = value.UInt(0); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.UInt(0); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.UInt(0); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, reg_value.value.vector.uint8, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, reg_value.value.vector.uint8, 16); + return false; + + case exc_trapno: + exc.trapno = value.UInt(0); + break; + + case exc_err: + exc.err = value.UInt(0); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.UInt(0); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_i386::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + int set = RegisterContextMach_i386::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + data.SetData(&gpr.eax + reg - gpr_eax, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fcw: + data.SetData(&fpu.fcw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fsw: + data.SetData(&fpu.fsw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ftw: + data.SetData(&fpu.ftw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fop: + data.SetData(&fpu.fop, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ip: + data.SetData(&fpu.ip, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_cs: + data.SetData(&fpu.cs, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_dp: + data.SetData(&fpu.dp, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ds: + data.SetData(&fpu.ds, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsr: + data.SetData(&fpu.mxcsr, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsrmask: + data.SetData(&fpu.mxcsrmask, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + data.SetData(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + data.SetData(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case exc_trapno: + data.SetData(&exc.trapno, reg_info->byte_size, eByteOrderHost); + break; + + case exc_err: + data.SetData(&exc.err, reg_info->byte_size, eByteOrderHost); + break; + + case exc_faultvaddr: + data.SetData(&exc.faultvaddr, reg_info->byte_size, eByteOrderHost); + break; + + default: + return false; + } + return true; +} + +bool +RegisterContextMach_i386::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size)) + return false; + + uint32_t offset = data_offset; + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + (&gpr.eax)[reg - gpr_eax] = data.GetU32 (&offset); + break; + + case fpu_fcw: + fpu.fcw = data.GetU16(&offset); + break; + + case fpu_fsw: + fpu.fsw = data.GetU16(&offset); + break; + + case fpu_ftw: + fpu.ftw = data.GetU8(&offset); + break; + + case fpu_fop: + fpu.fop = data.GetU16(&offset); + break; + + case fpu_ip: + fpu.ip = data.GetU32(&offset); + break; + + case fpu_cs: + fpu.cs = data.GetU16(&offset); + break; + + case fpu_dp: + fpu.dp = data.GetU32(&offset); + break; + + case fpu_ds: + fpu.ds = data.GetU16(&offset); + break; + + case fpu_mxcsr: + fpu.mxcsr = data.GetU32(&offset); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = data.GetU32(&offset); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case exc_trapno: + exc.trapno = data.GetU32 (&offset); + break; + + case exc_err: + exc.err = data.GetU32 (&offset); + break; + + case exc_faultvaddr: + exc.faultvaddr = data.GetU32 (&offset); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_i386::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextMach_i386::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + + +uint32_t +RegisterContextMach_i386::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } + else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (reg) + { + case dwarf_eax: return gpr_eax; + case dwarf_ecx: return gpr_ecx; + case dwarf_edx: return gpr_edx; + case dwarf_ebx: return gpr_ebx; + case dwarf_esp: return gpr_esp; + case dwarf_ebp: return gpr_ebp; + case dwarf_esi: return gpr_esi; + case dwarf_edi: return gpr_edi; + case dwarf_eip: return gpr_eip; + case dwarf_eflags: return gpr_eflags; + case dwarf_stmm0: return fpu_stmm0; + case dwarf_stmm1: return fpu_stmm1; + case dwarf_stmm2: return fpu_stmm2; + case dwarf_stmm3: return fpu_stmm3; + case dwarf_stmm4: return fpu_stmm4; + case dwarf_stmm5: return fpu_stmm5; + case dwarf_stmm6: return fpu_stmm6; + case dwarf_stmm7: return fpu_stmm7; + case dwarf_xmm0: return fpu_xmm0; + case dwarf_xmm1: return fpu_xmm1; + case dwarf_xmm2: return fpu_xmm2; + case dwarf_xmm3: return fpu_xmm3; + case dwarf_xmm4: return fpu_xmm4; + case dwarf_xmm5: return fpu_xmm5; + case dwarf_xmm6: return fpu_xmm6; + case dwarf_xmm7: return fpu_xmm7; + default: + break; + } + } + else if (kind == eRegisterKindGDB) + { + switch (reg) + { + case gdb_eax : return gpr_eax; + case gdb_ebx : return gpr_ebx; + case gdb_ecx : return gpr_ecx; + case gdb_edx : return gpr_edx; + case gdb_esi : return gpr_esi; + case gdb_edi : return gpr_edi; + case gdb_ebp : return gpr_ebp; + case gdb_esp : return gpr_esp; + case gdb_eip : return gpr_eip; + case gdb_eflags : return gpr_eflags; + case gdb_cs : return gpr_cs; + case gdb_ss : return gpr_ss; + case gdb_ds : return gpr_ds; + case gdb_es : return gpr_es; + case gdb_fs : return gpr_fs; + case gdb_gs : return gpr_gs; + case gdb_stmm0 : return fpu_stmm0; + case gdb_stmm1 : return fpu_stmm1; + case gdb_stmm2 : return fpu_stmm2; + case gdb_stmm3 : return fpu_stmm3; + case gdb_stmm4 : return fpu_stmm4; + case gdb_stmm5 : return fpu_stmm5; + case gdb_stmm6 : return fpu_stmm6; + case gdb_stmm7 : return fpu_stmm7; + case gdb_fctrl : return fpu_fctrl; + case gdb_fstat : return fpu_fstat; + case gdb_ftag : return fpu_ftag; + case gdb_fiseg : return fpu_fiseg; + case gdb_fioff : return fpu_fioff; + case gdb_foseg : return fpu_foseg; + case gdb_fooff : return fpu_fooff; + case gdb_fop : return fpu_fop; + case gdb_xmm0 : return fpu_xmm0; + case gdb_xmm1 : return fpu_xmm1; + case gdb_xmm2 : return fpu_xmm2; + case gdb_xmm3 : return fpu_xmm3; + case gdb_xmm4 : return fpu_xmm4; + case gdb_xmm5 : return fpu_xmm5; + case gdb_xmm6 : return fpu_xmm6; + case gdb_xmm7 : return fpu_xmm7; + case gdb_mxcsr : return fpu_mxcsr; + default: + break; + } + } + return LLDB_INVALID_REGNUM; +} + + +bool +RegisterContextMach_i386::HardwareSingleStep (bool enable) +{ + if (ReadGPR(false) != KERN_SUCCESS) + return false; + + const uint32_t trace_bit = 0x100u; + if (enable) + { + // If the trace bit is already set, there is nothing to do + if (gpr.eflags & trace_bit) + return true; + else + gpr.eflags |= trace_bit; + } + else + { + // If the trace bit is already cleared, there is nothing to do + if (gpr.eflags & trace_bit) + gpr.eflags &= ~trace_bit; + else + return true; + } + + return WriteGPR() == KERN_SUCCESS; +} + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h new file mode 100644 index 000000000000..580186752a9e --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h @@ -0,0 +1,256 @@ +//===-- RegisterContextMach_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_RegisterContextMach_i386_h_ +#define liblldb_RegisterContextMach_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +class RegisterContextMach_i386 : public lldb_private::RegisterContext +{ +public: + + RegisterContextMach_i386(lldb_private::Thread &thread, + lldb_private::StackFrame *frame); + + virtual + ~RegisterContextMach_i386(); + + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual bool + HardwareSingleStep (bool enable); + + struct GPR + { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ss; + uint32_t eflags; + uint32_t eip; + uint32_t cs; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + }; + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint32_t pad[2]; + uint16_t fcw; + uint16_t fsw; + uint8_t ftw; + uint8_t pad1; + uint16_t fop; + uint32_t ip; + uint16_t cs; + uint16_t pad2; + uint32_t dp; + uint16_t ds; + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[8]; + uint8_t pad4[14*16]; + int pad5; + }; + + struct EXC + { + uint32_t trapno; + uint32_t err; + uint32_t faultvaddr; + }; + +protected: + + enum + { + GPRRegSet = 1, + FPURegSet = 2, + EXCRegSet = 3 + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + kern_return_t + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, kern_return_t err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == KERN_SUCCESS; + } + + void + LogGPR (lldb_private::Log *log, const char *title); + + kern_return_t + ReadGPR (bool force); + + kern_return_t + ReadFPU (bool force); + + kern_return_t + ReadEXC (bool force); + + kern_return_t + WriteGPR (); + + kern_return_t + WriteFPU (); + + kern_return_t + WriteEXC (); + + kern_return_t + ReadRegisterSet (uint32_t set, bool force); + + kern_return_t + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb::RegisterInfo * + GetRegisterInfos (); +}; + +#endif // liblldb_RegisterContextMach_i386_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp new file mode 100644 index 000000000000..a7ed32e649a5 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp @@ -0,0 +1,1328 @@ +//===-- RegisterContextMach_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. +// +//===----------------------------------------------------------------------===// + + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" + +// Project includes +#include "RegisterContextMach_x86_64.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_rax = 0, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp, +}; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_gpr_rax = 0, + gcc_dwarf_gpr_rdx, + gcc_dwarf_gpr_rcx, + gcc_dwarf_gpr_rbx, + gcc_dwarf_gpr_rsi, + gcc_dwarf_gpr_rdi, + gcc_dwarf_gpr_rbp, + gcc_dwarf_gpr_rsp, + gcc_dwarf_gpr_r8, + gcc_dwarf_gpr_r9, + gcc_dwarf_gpr_r10, + gcc_dwarf_gpr_r11, + gcc_dwarf_gpr_r12, + gcc_dwarf_gpr_r13, + gcc_dwarf_gpr_r14, + gcc_dwarf_gpr_r15, + gcc_dwarf_gpr_rip, + gcc_dwarf_fpu_xmm0, + gcc_dwarf_fpu_xmm1, + gcc_dwarf_fpu_xmm2, + gcc_dwarf_fpu_xmm3, + gcc_dwarf_fpu_xmm4, + gcc_dwarf_fpu_xmm5, + gcc_dwarf_fpu_xmm6, + gcc_dwarf_fpu_xmm7, + gcc_dwarf_fpu_xmm8, + gcc_dwarf_fpu_xmm9, + gcc_dwarf_fpu_xmm10, + gcc_dwarf_fpu_xmm11, + gcc_dwarf_fpu_xmm12, + gcc_dwarf_fpu_xmm13, + gcc_dwarf_fpu_xmm14, + gcc_dwarf_fpu_xmm15, + gcc_dwarf_fpu_stmm0, + gcc_dwarf_fpu_stmm1, + gcc_dwarf_fpu_stmm2, + gcc_dwarf_fpu_stmm3, + gcc_dwarf_fpu_stmm4, + gcc_dwarf_fpu_stmm5, + gcc_dwarf_fpu_stmm6, + gcc_dwarf_fpu_stmm7, + +}; + +enum gdb_regnums +{ + gdb_gpr_rax = 0, + gdb_gpr_rbx = 1, + gdb_gpr_rcx = 2, + gdb_gpr_rdx = 3, + gdb_gpr_rsi = 4, + gdb_gpr_rdi = 5, + gdb_gpr_rbp = 6, + gdb_gpr_rsp = 7, + gdb_gpr_r8 = 8, + gdb_gpr_r9 = 9, + gdb_gpr_r10 = 10, + gdb_gpr_r11 = 11, + gdb_gpr_r12 = 12, + gdb_gpr_r13 = 13, + gdb_gpr_r14 = 14, + gdb_gpr_r15 = 15, + gdb_gpr_rip = 16, + gdb_gpr_rflags = 17, + gdb_gpr_cs = 18, + gdb_gpr_ss = 19, + gdb_gpr_ds = 20, + gdb_gpr_es = 21, + gdb_gpr_fs = 22, + gdb_gpr_gs = 23, + gdb_fpu_stmm0 = 24, + gdb_fpu_stmm1 = 25, + gdb_fpu_stmm2 = 26, + gdb_fpu_stmm3 = 27, + gdb_fpu_stmm4 = 28, + gdb_fpu_stmm5 = 29, + gdb_fpu_stmm6 = 30, + gdb_fpu_stmm7 = 31, + gdb_fpu_fctrl = 32, gdb_fpu_fcw = gdb_fpu_fctrl, + gdb_fpu_fstat = 33, gdb_fpu_fsw = gdb_fpu_fstat, + gdb_fpu_ftag = 34, gdb_fpu_ftw = gdb_fpu_ftag, + gdb_fpu_fiseg = 35, gdb_fpu_cs = gdb_fpu_fiseg, + gdb_fpu_fioff = 36, gdb_fpu_ip = gdb_fpu_fioff, + gdb_fpu_foseg = 37, gdb_fpu_ds = gdb_fpu_foseg, + gdb_fpu_fooff = 38, gdb_fpu_dp = gdb_fpu_fooff, + gdb_fpu_fop = 39, + gdb_fpu_xmm0 = 40, + gdb_fpu_xmm1 = 41, + gdb_fpu_xmm2 = 42, + gdb_fpu_xmm3 = 43, + gdb_fpu_xmm4 = 44, + gdb_fpu_xmm5 = 45, + gdb_fpu_xmm6 = 46, + gdb_fpu_xmm7 = 47, + gdb_fpu_xmm8 = 48, + gdb_fpu_xmm9 = 49, + gdb_fpu_xmm10 = 50, + gdb_fpu_xmm11 = 51, + gdb_fpu_xmm12 = 52, + gdb_fpu_xmm13 = 53, + gdb_fpu_xmm14 = 54, + gdb_fpu_xmm15 = 55, + gdb_fpu_mxcsr = 56, +}; + +RegisterContextMach_x86_64::RegisterContextMach_x86_64 (Thread &thread, StackFrame *frame) : + RegisterContext (thread, frame), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; ireg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, gpr_##reg +#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextMach_x86_64::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex, fpu_##reg +#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextMach_x86_64::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, fpu_##reg##i, { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, gdb_fpu_##reg##i } +#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextMach_x86_64::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex, exc_##reg + +#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_x86_64::GPR) + sizeof (RegisterContextMach_x86_64::FPU) + sizeof (RegisterContextMach_x86_64::EXC)) + +// General purpose registers for 64 bit +static RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC REG KIND NUM DWARF REG KIND NUM GENERIC REG KIND NUM GDB REG KIND NUM +// =============================== ======================= =================== ========================== ========================== + { DEFINE_GPR (rax , NULL) , { gcc_dwarf_gpr_rax , gcc_dwarf_gpr_rax , LLDB_INVALID_REGNUM , gdb_gpr_rax }}, + { DEFINE_GPR (rbx , NULL) , { gcc_dwarf_gpr_rbx , gcc_dwarf_gpr_rbx , LLDB_INVALID_REGNUM , gdb_gpr_rbx }}, + { DEFINE_GPR (rcx , NULL) , { gcc_dwarf_gpr_rcx , gcc_dwarf_gpr_rcx , LLDB_INVALID_REGNUM , gdb_gpr_rcx }}, + { DEFINE_GPR (rdx , NULL) , { gcc_dwarf_gpr_rdx , gcc_dwarf_gpr_rdx , LLDB_INVALID_REGNUM , gdb_gpr_rdx }}, + { DEFINE_GPR (rdi , NULL) , { gcc_dwarf_gpr_rdi , gcc_dwarf_gpr_rdi , LLDB_INVALID_REGNUM , gdb_gpr_rdi }}, + { DEFINE_GPR (rsi , NULL) , { gcc_dwarf_gpr_rsi , gcc_dwarf_gpr_rsi , LLDB_INVALID_REGNUM , gdb_gpr_rsi }}, + { DEFINE_GPR (rbp , "fp") , { gcc_dwarf_gpr_rbp , gcc_dwarf_gpr_rbp , LLDB_REGNUM_GENERIC_FP , gdb_gpr_rbp }}, + { DEFINE_GPR (rsp , "sp") , { gcc_dwarf_gpr_rsp , gcc_dwarf_gpr_rsp , LLDB_REGNUM_GENERIC_SP , gdb_gpr_rsp }}, + { DEFINE_GPR (r8 , NULL) , { gcc_dwarf_gpr_r8 , gcc_dwarf_gpr_r8 , LLDB_INVALID_REGNUM , gdb_gpr_r8 }}, + { DEFINE_GPR (r9 , NULL) , { gcc_dwarf_gpr_r9 , gcc_dwarf_gpr_r9 , LLDB_INVALID_REGNUM , gdb_gpr_r9 }}, + { DEFINE_GPR (r10 , NULL) , { gcc_dwarf_gpr_r10 , gcc_dwarf_gpr_r10 , LLDB_INVALID_REGNUM , gdb_gpr_r10 }}, + { DEFINE_GPR (r11 , NULL) , { gcc_dwarf_gpr_r11 , gcc_dwarf_gpr_r11 , LLDB_INVALID_REGNUM , gdb_gpr_r11 }}, + { DEFINE_GPR (r12 , NULL) , { gcc_dwarf_gpr_r12 , gcc_dwarf_gpr_r12 , LLDB_INVALID_REGNUM , gdb_gpr_r12 }}, + { DEFINE_GPR (r13 , NULL) , { gcc_dwarf_gpr_r13 , gcc_dwarf_gpr_r13 , LLDB_INVALID_REGNUM , gdb_gpr_r13 }}, + { DEFINE_GPR (r14 , NULL) , { gcc_dwarf_gpr_r14 , gcc_dwarf_gpr_r14 , LLDB_INVALID_REGNUM , gdb_gpr_r14 }}, + { DEFINE_GPR (r15 , NULL) , { gcc_dwarf_gpr_r15 , gcc_dwarf_gpr_r15 , LLDB_INVALID_REGNUM , gdb_gpr_r15 }}, + { DEFINE_GPR (rip , "pc") , { gcc_dwarf_gpr_rip , gcc_dwarf_gpr_rip , LLDB_REGNUM_GENERIC_PC , gdb_gpr_rip }}, + { DEFINE_GPR (rflags, "flags") , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_gpr_rflags}}, + { DEFINE_GPR (cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_cs }}, + { DEFINE_GPR (fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_fs }}, + { DEFINE_GPR (gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_gs }}, + + { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fcw }}, + { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fsw }}, + { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ftw }}, + { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fop }}, + { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ip }}, + { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_cs }}, + { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_dp }}, + { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ds }}, + { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_mxcsr }}, + { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_FPU_VECT(stmm,0) }, + { DEFINE_FPU_VECT(stmm,1) }, + { DEFINE_FPU_VECT(stmm,2) }, + { DEFINE_FPU_VECT(stmm,3) }, + { DEFINE_FPU_VECT(stmm,4) }, + { DEFINE_FPU_VECT(stmm,5) }, + { DEFINE_FPU_VECT(stmm,6) }, + { DEFINE_FPU_VECT(stmm,7) }, + { DEFINE_FPU_VECT(xmm,0) }, + { DEFINE_FPU_VECT(xmm,1) }, + { DEFINE_FPU_VECT(xmm,2) }, + { DEFINE_FPU_VECT(xmm,3) }, + { DEFINE_FPU_VECT(xmm,4) }, + { DEFINE_FPU_VECT(xmm,5) }, + { DEFINE_FPU_VECT(xmm,6) }, + { DEFINE_FPU_VECT(xmm,7) }, + { DEFINE_FPU_VECT(xmm,8) }, + { DEFINE_FPU_VECT(xmm,9) }, + { DEFINE_FPU_VECT(xmm,10) }, + { DEFINE_FPU_VECT(xmm,11) }, + { DEFINE_FPU_VECT(xmm,12) }, + { DEFINE_FPU_VECT(xmm,13) }, + { DEFINE_FPU_VECT(xmm,14) }, + { DEFINE_FPU_VECT(xmm,15) }, + + { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }} +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + + +void +RegisterContextMach_x86_64::Invalidate () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextMach_x86_64::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + + +const RegisterInfo * +RegisterContextMach_x86_64::GetRegisterInfoAtIndex (uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + + +size_t +RegisterContextMach_x86_64::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextMach_x86_64::GetRegisterInfos () +{ + return g_register_infos; +} + + + +static uint32_t g_gpr_regnums[] = +{ + gpr_rax, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs +}; + +static uint32_t g_fpu_regnums[] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15 +}; + +static uint32_t +g_exc_regnums[] = +{ + exc_trapno, + exc_err, + exc_faultvaddr +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextMach_x86_64::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextMach_x86_64::GetRegisterSet (uint32_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + +int +RegisterContextMach_x86_64::GetSetForNativeRegNum (int reg_num) +{ + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + +void +RegisterContextMach_x86_64::LogGPR(Log *log, const char *format, ...) +{ + if (log) + { + if (format) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } + for (uint32_t i=0; iPrintf("%12s = 0x%16.16llx", g_register_infos[reg].name, (&gpr.rax)[reg]); + } + } +} + +kern_return_t +RegisterContextMach_x86_64::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = GPRWordCount; + SetError(GPRRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count)); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log) + LogGPR (log, "RegisterContextMach_x86_64::ReadGPR(thread = 0x%4.4x)", GetThreadID()); + } + return GetError(GPRRegSet, Read); +} + +kern_return_t +RegisterContextMach_x86_64::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = FPUWordCount; + SetError(FPURegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count)); + } + return GetError(FPURegSet, Read); +} + +kern_return_t +RegisterContextMach_x86_64::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = EXCWordCount; + SetError(EXCRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count)); + } + return GetError(EXCRegSet, Read); +} + +kern_return_t +RegisterContextMach_x86_64::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log) + LogGPR (log, "RegisterContextMach_x86_64::WriteGPR (thread = 0x%4.4x)", GetThreadID()); + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +kern_return_t +RegisterContextMach_x86_64::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +kern_return_t +RegisterContextMach_x86_64::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +kern_return_t +RegisterContextMach_x86_64::ReadRegisterSet(uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR (force); + case FPURegSet: return ReadFPU (force); + case EXCRegSet: return ReadEXC (force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +RegisterContextMach_x86_64::WriteRegisterSet(uint32_t set) +{ + // Make sure we have a valid context to set. + switch (set) + { + case GPRRegSet: return WriteGPR (); + case FPURegSet: return WriteFPU (); + case EXCRegSet: return WriteEXC (); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + + +bool +RegisterContextMach_x86_64::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + value = (&gpr.rax)[reg - gpr_rax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these + // registers + //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16); + return false; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + + +bool +RegisterContextMach_x86_64::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + (&gpr.rax)[reg - gpr_rax] = value.ULongLong(0); + break; + + case fpu_fcw: + fpu.fcw = value.UInt(0); + break; + + case fpu_fsw: + fpu.fsw = value.UInt(0); + break; + + case fpu_ftw: + fpu.ftw = value.UInt(0); + break; + + case fpu_fop: + fpu.fop = value.UInt(0); + break; + + case fpu_ip: + fpu.ip = value.UInt(0); + break; + + case fpu_cs: + fpu.cs = value.UInt(0); + break; + + case fpu_dp: + fpu.dp = value.UInt(0); + break; + + case fpu_ds: + fpu.ds = value.UInt(0); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.UInt(0); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.UInt(0); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, reg_value.value.vector.uint8, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, reg_value.value.vector.uint8, 16); + return false; + + case exc_trapno: + exc.trapno = value.UInt(0); + break; + + case exc_err: + exc.err = value.UInt(0); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.UInt(0); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_x86_64::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + data.SetData(&gpr.rax + reg - gpr_rax, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fcw: + data.SetData(&fpu.fcw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fsw: + data.SetData(&fpu.fsw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ftw: + data.SetData(&fpu.ftw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fop: + data.SetData(&fpu.fop, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ip: + data.SetData(&fpu.ip, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_cs: + data.SetData(&fpu.cs, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_dp: + data.SetData(&fpu.dp, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ds: + data.SetData(&fpu.ds, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsr: + data.SetData(&fpu.mxcsr, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsrmask: + data.SetData(&fpu.mxcsrmask, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + data.SetData(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + data.SetData(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case exc_trapno: + data.SetData(&exc.trapno, reg_info->byte_size, eByteOrderHost); + break; + + case exc_err: + data.SetData(&exc.err, reg_info->byte_size, eByteOrderHost); + break; + + case exc_faultvaddr: + data.SetData(&exc.faultvaddr, reg_info->byte_size, eByteOrderHost); + break; + + default: + return false; + } + return true; +} + +bool +RegisterContextMach_x86_64::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size)) + return false; + + uint32_t offset = data_offset; + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + (&gpr.rax)[reg - gpr_rax] = data.GetU32 (&offset); + break; + + case fpu_fcw: + fpu.fcw = data.GetU16(&offset); + break; + + case fpu_fsw: + fpu.fsw = data.GetU16(&offset); + break; + + case fpu_ftw: + fpu.ftw = data.GetU8(&offset); + break; + + case fpu_fop: + fpu.fop = data.GetU16(&offset); + break; + + case fpu_ip: + fpu.ip = data.GetU32(&offset); + break; + + case fpu_cs: + fpu.cs = data.GetU16(&offset); + break; + + case fpu_dp: + fpu.dp = data.GetU32(&offset); + break; + + case fpu_ds: + fpu.ds = data.GetU16(&offset); + break; + + case fpu_mxcsr: + fpu.mxcsr = data.GetU32(&offset); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = data.GetU32(&offset); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case exc_trapno: + exc.trapno = data.GetU32 (&offset); + break; + + case exc_err: + exc.err = data.GetU32 (&offset); + break; + + case exc_faultvaddr: + exc.faultvaddr = data.GetU32 (&offset); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextMach_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + + +uint32_t +RegisterContextMach_x86_64::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_rip; + case LLDB_REGNUM_GENERIC_SP: return gpr_rsp; + case LLDB_REGNUM_GENERIC_FP: return gpr_rbp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } + else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (reg) + { + case gcc_dwarf_gpr_rax: return gpr_rax; + case gcc_dwarf_gpr_rdx: return gpr_rdx; + case gcc_dwarf_gpr_rcx: return gpr_rcx; + case gcc_dwarf_gpr_rbx: return gpr_rbx; + case gcc_dwarf_gpr_rsi: return gpr_rsi; + case gcc_dwarf_gpr_rdi: return gpr_rdi; + case gcc_dwarf_gpr_rbp: return gpr_rbp; + case gcc_dwarf_gpr_rsp: return gpr_rsp; + case gcc_dwarf_gpr_r8: return gpr_r8; + case gcc_dwarf_gpr_r9: return gpr_r9; + case gcc_dwarf_gpr_r10: return gpr_r10; + case gcc_dwarf_gpr_r11: return gpr_r11; + case gcc_dwarf_gpr_r12: return gpr_r12; + case gcc_dwarf_gpr_r13: return gpr_r13; + case gcc_dwarf_gpr_r14: return gpr_r14; + case gcc_dwarf_gpr_r15: return gpr_r15; + case gcc_dwarf_gpr_rip: return gpr_rip; + case gcc_dwarf_fpu_xmm0: return fpu_xmm0; + case gcc_dwarf_fpu_xmm1: return fpu_xmm1; + case gcc_dwarf_fpu_xmm2: return fpu_xmm2; + case gcc_dwarf_fpu_xmm3: return fpu_xmm3; + case gcc_dwarf_fpu_xmm4: return fpu_xmm4; + case gcc_dwarf_fpu_xmm5: return fpu_xmm5; + case gcc_dwarf_fpu_xmm6: return fpu_xmm6; + case gcc_dwarf_fpu_xmm7: return fpu_xmm7; + case gcc_dwarf_fpu_xmm8: return fpu_xmm8; + case gcc_dwarf_fpu_xmm9: return fpu_xmm9; + case gcc_dwarf_fpu_xmm10: return fpu_xmm10; + case gcc_dwarf_fpu_xmm11: return fpu_xmm11; + case gcc_dwarf_fpu_xmm12: return fpu_xmm12; + case gcc_dwarf_fpu_xmm13: return fpu_xmm13; + case gcc_dwarf_fpu_xmm14: return fpu_xmm14; + case gcc_dwarf_fpu_xmm15: return fpu_xmm15; + case gcc_dwarf_fpu_stmm0: return fpu_stmm0; + case gcc_dwarf_fpu_stmm1: return fpu_stmm1; + case gcc_dwarf_fpu_stmm2: return fpu_stmm2; + case gcc_dwarf_fpu_stmm3: return fpu_stmm3; + case gcc_dwarf_fpu_stmm4: return fpu_stmm4; + case gcc_dwarf_fpu_stmm5: return fpu_stmm5; + case gcc_dwarf_fpu_stmm6: return fpu_stmm6; + case gcc_dwarf_fpu_stmm7: return fpu_stmm7; + default: + break; + } + } + else if (kind == eRegisterKindGDB) + { + switch (reg) + { + case gdb_gpr_rax : return gpr_rax; + case gdb_gpr_rbx : return gpr_rbx; + case gdb_gpr_rcx : return gpr_rcx; + case gdb_gpr_rdx : return gpr_rdx; + case gdb_gpr_rsi : return gpr_rsi; + case gdb_gpr_rdi : return gpr_rdi; + case gdb_gpr_rbp : return gpr_rbp; + case gdb_gpr_rsp : return gpr_rsp; + case gdb_gpr_r8 : return gpr_r8; + case gdb_gpr_r9 : return gpr_r9; + case gdb_gpr_r10 : return gpr_r10; + case gdb_gpr_r11 : return gpr_r11; + case gdb_gpr_r12 : return gpr_r12; + case gdb_gpr_r13 : return gpr_r13; + case gdb_gpr_r14 : return gpr_r14; + case gdb_gpr_r15 : return gpr_r15; + case gdb_gpr_rip : return gpr_rip; + case gdb_gpr_rflags : return gpr_rflags; + case gdb_gpr_cs : return gpr_cs; + case gdb_gpr_ss : return gpr_gs; // HACK: For now for "ss", just copy what is in "gs" + case gdb_gpr_ds : return gpr_gs; // HACK: For now for "ds", just copy what is in "gs" + case gdb_gpr_es : return gpr_gs; // HACK: For now for "es", just copy what is in "gs" + case gdb_gpr_fs : return gpr_fs; + case gdb_gpr_gs : return gpr_gs; + case gdb_fpu_stmm0 : return fpu_stmm0; + case gdb_fpu_stmm1 : return fpu_stmm1; + case gdb_fpu_stmm2 : return fpu_stmm2; + case gdb_fpu_stmm3 : return fpu_stmm3; + case gdb_fpu_stmm4 : return fpu_stmm4; + case gdb_fpu_stmm5 : return fpu_stmm5; + case gdb_fpu_stmm6 : return fpu_stmm6; + case gdb_fpu_stmm7 : return fpu_stmm7; + case gdb_fpu_fctrl : return fpu_fctrl; + case gdb_fpu_fstat : return fpu_fstat; + case gdb_fpu_ftag : return fpu_ftag; + case gdb_fpu_fiseg : return fpu_fiseg; + case gdb_fpu_fioff : return fpu_fioff; + case gdb_fpu_foseg : return fpu_foseg; + case gdb_fpu_fooff : return fpu_fooff; + case gdb_fpu_fop : return fpu_fop; + case gdb_fpu_xmm0 : return fpu_xmm0; + case gdb_fpu_xmm1 : return fpu_xmm1; + case gdb_fpu_xmm2 : return fpu_xmm2; + case gdb_fpu_xmm3 : return fpu_xmm3; + case gdb_fpu_xmm4 : return fpu_xmm4; + case gdb_fpu_xmm5 : return fpu_xmm5; + case gdb_fpu_xmm6 : return fpu_xmm6; + case gdb_fpu_xmm7 : return fpu_xmm7; + case gdb_fpu_xmm8 : return fpu_xmm8; + case gdb_fpu_xmm9 : return fpu_xmm9; + case gdb_fpu_xmm10 : return fpu_xmm10; + case gdb_fpu_xmm11 : return fpu_xmm11; + case gdb_fpu_xmm12 : return fpu_xmm12; + case gdb_fpu_xmm13 : return fpu_xmm13; + case gdb_fpu_xmm14 : return fpu_xmm14; + case gdb_fpu_xmm15 : return fpu_xmm15; + case gdb_fpu_mxcsr : return fpu_mxcsr; + default: + break; + } + } + return LLDB_INVALID_REGNUM; +} + +bool +RegisterContextMach_x86_64::HardwareSingleStep (bool enable) +{ + if (ReadGPR(true) != KERN_SUCCESS) + return false; + + const uint64_t trace_bit = 0x100ull; + if (enable) + { + + if (gpr.rflags & trace_bit) + return true; // trace bit is already set, there is nothing to do + else + gpr.rflags |= trace_bit; + } + else + { + if (gpr.rflags & trace_bit) + gpr.rflags &= ~trace_bit; + else + return true; // trace bit is clear, there is nothing to do + } + + return WriteGPR() == KERN_SUCCESS; +} + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h new file mode 100644 index 000000000000..4f33bbdc2db5 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h @@ -0,0 +1,261 @@ +//===-- RegisterContextMach_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_RegisterContextMach_x86_64_h_ +#define liblldb_RegisterContextMach_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +class RegisterContextMach_x86_64 : public lldb_private::RegisterContext +{ +public: + RegisterContextMach_x86_64 (lldb_private::Thread &thread, + lldb_private::StackFrame *frame); + + virtual + ~RegisterContextMach_x86_64(); + + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual bool + HardwareSingleStep (bool enable); + + struct GPR + { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; + }; + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint32_t pad[2]; + uint16_t fcw; // "fctrl" + uint16_t fsw; // "fstat" + uint8_t ftw; // "ftag" + uint8_t pad1; + uint16_t fop; // "fop" + uint32_t ip; // "fioff" + uint16_t cs; // "fiseg" + uint16_t pad2; + uint32_t dp; // "fooff" + uint16_t ds; // "foseg" + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[16]; + uint8_t pad4[6*16]; + int pad5; + }; + + struct EXC + { + uint32_t trapno; + uint32_t err; + uint64_t faultvaddr; + }; + +protected: + + typedef enum + { + GPRRegSet = 4, + FPURegSet = 5, + EXCRegSet = 6 + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + kern_return_t + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, kern_return_t err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == KERN_SUCCESS; + } + + void + LogGPR (lldb_private::Log *log, const char *format, ...); + + kern_return_t + ReadGPR (bool force); + + kern_return_t + ReadFPU (bool force); + + kern_return_t + ReadEXC (bool force); + + kern_return_t + WriteGPR (); + + kern_return_t + WriteFPU (); + + kern_return_t + WriteEXC (); + + kern_return_t + ReadRegisterSet (uint32_t set, bool force); + + kern_return_t + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb::RegisterInfo * + GetRegisterInfos (); + +}; + +#endif // liblldb_RegisterContextMach_x86_64_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp new file mode 100644 index 000000000000..46d84a853d0a --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp @@ -0,0 +1,769 @@ +//===-- ThreadMacOSX.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadMacOSX.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" +#include "MachThreadContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Breakpoint/WatchpointLocation.h" +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadMacOSX::ThreadMacOSX (ProcessMacOSX &process, lldb::tid_t tid) : + Thread(process, tid), + m_fp_pc_pairs(), + m_basic_info(), + m_suspend_count(0), + m_stop_exception(), + m_context() +{ + ProcessMacOSX::CreateArchCalback create_arch_callback = process.GetArchCreateCallback(); + assert(create_arch_callback != NULL); + m_context.reset(create_arch_callback(process.GetArchSpec(), *this)); + assert(m_context.get() != NULL); + m_context->InitializeInstance(); + ::bzero (&m_basic_info, sizeof (m_basic_info)); + ::bzero (&m_ident_info, sizeof (m_ident_info)); + ::bzero (&m_proc_threadinfo, sizeof (m_proc_threadinfo)); + ProcessMacOSXLog::LogIf(PD_LOG_THREAD | PD_LOG_VERBOSE, "ThreadMacOSX::ThreadMacOSX ( pid = %i, tid = 0x%4.4x, )", m_process.GetID(), GetID()); +} + +ThreadMacOSX::~ThreadMacOSX () +{ +} + +#if defined (__i386__) || defined (__x86_64__) + #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_I386_BPT + #define MACH_TRAP_DATA_0 EXC_I386_SGL +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_PPC_BREAKPOINT + +#elif defined (__arm__) + #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_ARM_BREAKPOINT +#endif + + +bool +ThreadMacOSX::GetRawStopReason (Thread::StopInfo *stop_info ) +{ + stop_info->SetThread(this); + + bool success = GetStopException().GetStopInfo(stop_info); + + +#if defined (MACH_SOFTWARE_BREAKPOINT_DATA_0) || defined (MACH_TRAP_DATA_0) + if (stop_info->GetStopReason() == eStopReasonException) + { + if (stop_info->GetExceptionType() == EXC_BREAKPOINT && stop_info->GetExceptionDataCount() == 2) + { + const lldb::addr_t data_0 = stop_info->GetExceptionDataAtIndex(0); +#if defined (MACH_SOFTWARE_BREAKPOINT_DATA_0) + if (data_0 == MACH_SOFTWARE_BREAKPOINT_DATA_0) + { + lldb::addr_t pc = GetRegisterContext()->GetPC(); + lldb::user_id_t break_id = m_process.GetBreakpointSiteList().FindIDByAddress(pc); + if (break_id != LLDB_INVALID_BREAK_ID) + { + stop_info->Clear (); + stop_info->SetStopReasonWithBreakpointSiteID (break_id); + return success; + } + } +#endif +#if defined (MACH_TRAP_DATA_0) + if (data_0 == MACH_TRAP_DATA_0) + { + stop_info->Clear (); + stop_info->SetStopReasonToTrace (); + return success; + } +#endif + } + } +#endif + + if (stop_info->GetStopReason() == eStopReasonException) + { + if (stop_info->GetExceptionType() == EXC_SOFTWARE && + stop_info->GetExceptionDataCount() == 2 && + stop_info->GetExceptionDataAtIndex(0) == EXC_SOFT_SIGNAL) + { + int signo = stop_info->GetExceptionDataAtIndex(1); + stop_info->Clear (); + stop_info->SetStopReasonWithSignal (signo); + } + } + else + { + stop_info->SetStopReasonToNone(); + } + + return success; +} + +const char * +ThreadMacOSX::GetInfo () +{ + return GetBasicInfoAsString(); +} + +bool +ThreadMacOSX::GetIdentifierInfo () +{ +#ifdef THREAD_IDENTIFIER_INFO_COUNT + if (m_ident_info.thread_id == 0) + { + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + return ::thread_info (GetID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count) == KERN_SUCCESS; + } +#else + //m_error.SetErrorString("Thread_info doesn't support THREAD_IDENTIFIER_INFO."); +#endif + + return false; +} + +const char * +ThreadMacOSX::GetDispatchQueueName() +{ + if (GetIdentifierInfo ()) + { + if (m_ident_info.dispatch_qaddr == 0) + return NULL; + + uint8_t memory_buffer[8]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), m_process.GetByteOrder(), m_process.GetAddressByteSize()); + ModuleSP module_sp(m_process.GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib"))); + if (module_sp.get() == NULL) + return NULL; + + lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; + const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData); + if (dispatch_queue_offsets_symbol) + dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(&GetProcess()); + + if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + return NULL; + + // Excerpt from src/queue_private.h + struct dispatch_queue_offsets_s + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + } dispatch_queue_offsets; + + Error error; + if (m_process.ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets)) + { + uint32_t data_offset = 0; + if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) + { + if (m_process.ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) + { + data_offset = 0; + lldb::addr_t queue_addr = data.GetAddress(&data_offset); + lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; + const size_t chunk_size = 32; + uint32_t label_pos = 0; + m_dispatch_queue_name.resize(chunk_size, '\0'); + while (1) + { + size_t bytes_read = m_process.ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size, error); + + if (bytes_read <= 0) + break; + + if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos) + break; + label_pos += bytes_read; + } + m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0')); + } + } + } + } + + if (m_dispatch_queue_name.empty()) + return NULL; + return m_dispatch_queue_name.c_str(); +} + +const char * +ThreadMacOSX::GetName () +{ + if (GetIdentifierInfo ()) + ::proc_pidinfo (m_process.GetID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo)); + + // No thread name, lets return the queue name instead + if (m_proc_threadinfo.pth_name[0] == '\0') + return GetDispatchQueueName(); + + // Return the thread name if there was one + if (m_proc_threadinfo.pth_name[0]) + return m_proc_threadinfo.pth_name; + return NULL; +} + +bool +ThreadMacOSX::WillResume (StateType resume_state) +{ + ThreadWillResume(resume_state); + Thread::WillResume(resume_state); + return true; +} + +void +ThreadMacOSX::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context + GetRegisterContext()->Invalidate(); + + m_context->RefreshStateAfterStop(); + + // We may have suspended this thread so the primary thread could step + // without worrying about race conditions, so lets restore our suspend + // count. + RestoreSuspendCount(); + + // Update the basic information for a thread for suspend count reasons. + ThreadMacOSX::GetBasicInfo(GetID(), &m_basic_info); + m_suspend_count = m_basic_info.suspend_count; + m_basic_info_string.clear(); +} + +uint32_t +ThreadMacOSX::GetStackFrameCount() +{ + if (m_fp_pc_pairs.empty()) + GetStackFrameData(m_fp_pc_pairs); + return m_fp_pc_pairs.size(); +} + +// Make sure that GetStackFrameAtIndex() does NOT call GetStackFrameCount() when +// getting the stack frame at index zero! This way GetStackFrameCount() (via +// GetStackFRameData()) can call this function to get the first frame in order +// to provide the first frame to a lower call for efficiency sake (avoid +// redundant lookups in the frame symbol context). +lldb::StackFrameSP +ThreadMacOSX::GetStackFrameAtIndex (uint32_t idx) +{ + StackFrameSP frame_sp(m_frames.GetFrameAtIndex(idx)); + + if (frame_sp) + return frame_sp; + + // Don't try and fetch a frame while process is running + // Calling IsRunning isn't right here, because IsRunning reads the Public + // state but we need to be able to read the stack frames in the ShouldStop + // methods, which happen before the Public state has been updated. +// if (m_process.IsRunning()) +// return frame_sp; + + // Special case the first frame (idx == 0) so that we don't need to + // know how many stack frames there are to get it. If we need any other + // frames, then we do need to know if "idx" is a valid index. + if (idx == 0) + { + // If this is the first frame, we want to share the thread register + // context with the stack frame at index zero. + GetRegisterContext(); + assert (m_reg_context_sp.get()); + frame_sp.reset (new StackFrame (idx, *this, m_reg_context_sp, m_reg_context_sp->GetFP(), m_reg_context_sp->GetPC())); + } + else if (idx < GetStackFrameCount()) + { + assert (idx < m_fp_pc_pairs.size()); + frame_sp.reset (new StackFrame (idx, *this, m_fp_pc_pairs[idx].first, m_fp_pc_pairs[idx].second)); + } + m_frames.SetFrameAtIndex(idx, frame_sp); + return frame_sp; +} + +void +ThreadMacOSX::ClearStackFrames () +{ + m_fp_pc_pairs.clear(); + Thread::ClearStackFrames(); +} + + + +int32_t +ThreadMacOSX::Suspend() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__); + lldb::tid_t tid = GetID (); + if (ThreadIDIsValid(tid)) + { + Error err(::thread_suspend (tid), eErrorTypeMachKernel); + if (err.Success()) + m_suspend_count++; + if (log || err.Fail()) + err.PutToLog(log, "::thread_suspend (%4.4x)", tid); + } + return GetSuspendCount(); +} + +int32_t +ThreadMacOSX::Resume() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ThreadMacOSX::%s ()", __FUNCTION__); + lldb::tid_t tid = GetID (); + if (ThreadIDIsValid(tid)) + { + while (m_suspend_count > 0) + { + Error err(::thread_resume (tid), eErrorTypeMachKernel); + if (err.Success()) + m_suspend_count--; + if (log || err.Fail()) + err.PutToLog(log, "::thread_resume (%4.4x)", tid); + } + } + return GetSuspendCount(); +} + +bool +ThreadMacOSX::RestoreSuspendCount() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__); + Error err; + lldb::tid_t tid = GetID (); + if (ThreadIDIsValid(tid) == false) + return false; + else if (m_suspend_count > m_basic_info.suspend_count) + { + while (m_suspend_count > m_basic_info.suspend_count) + { + err = ::thread_resume (tid); + if (err.Success()) + --m_suspend_count; + if (log || err.Fail()) + err.PutToLog(log, "::thread_resume (%4.4x)", tid); + } + } + else if (m_suspend_count < m_basic_info.suspend_count) + { + while (m_suspend_count < m_basic_info.suspend_count) + { + err = ::thread_suspend (tid); + if (err.Success()) + --m_suspend_count; + if (log || err.Fail()) + err.PutToLog(log, "::thread_suspend (%4.4x)", tid); + } + } + return m_suspend_count == m_basic_info.suspend_count; +} + + +const char * +ThreadMacOSX::GetBasicInfoAsString () +{ + if (m_basic_info_string.empty()) + { + StreamString sstr; + struct thread_basic_info basicInfo; + + lldb::tid_t tid = GetID (); + if (GetBasicInfo(tid, &basicInfo)) + { +// char run_state_str[32]; +// size_t run_state_str_size = sizeof(run_state_str); +// switch (basicInfo.run_state) +// { +// case TH_STATE_RUNNING: strncpy(run_state_str, "running", run_state_str_size); break; +// case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", run_state_str_size); break; +// case TH_STATE_WAITING: strncpy(run_state_str, "waiting", run_state_str_size); break; +// case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, "uninterruptible", run_state_str_size); break; +// case TH_STATE_HALTED: strncpy(run_state_str, "halted", run_state_str_size); break; +// default: snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break; // ??? +// } + float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f; + float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f; + sstr.Printf("Thread 0x%4.4x: user=%f system=%f cpu=%d sleep_time=%d", + InferiorThreadID(), + user, + system, + basicInfo.cpu_usage, + basicInfo.sleep_time); + m_basic_info_string.assign (sstr.GetData(), sstr.GetSize()); + } + } + if (m_basic_info_string.empty()) + return NULL; + return m_basic_info_string.c_str(); +} + + +//const uint8_t * +//ThreadMacOSX::SoftwareBreakpointOpcode (size_t break_op_size) const +//{ +// return m_context->SoftwareBreakpointOpcode(break_op_size); +//} + + +lldb::tid_t +ThreadMacOSX::InferiorThreadID() const +{ + mach_msg_type_number_t i; + mach_port_name_array_t names; + mach_port_type_array_t types; + mach_msg_type_number_t ncount, tcount; + lldb::tid_t inferior_tid = LLDB_INVALID_THREAD_ID; + task_t my_task = ::mach_task_self(); + task_t task = GetMacOSXProcess().Task().GetTaskPort(); + + kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount); + if (kret == KERN_SUCCESS) + { + lldb::tid_t tid = GetID (); + + for (i = 0; i < ncount; i++) + { + mach_port_t my_name; + mach_msg_type_name_t my_type; + + kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type); + if (kret == KERN_SUCCESS) + { + ::mach_port_deallocate (my_task, my_name); + if (my_name == tid) + { + inferior_tid = names[i]; + break; + } + } + } + // Free up the names and types + ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t)); + ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t)); + } + return inferior_tid; +} + +bool +ThreadMacOSX::GetBasicInfo(lldb::tid_t thread, struct thread_basic_info *basicInfoPtr) +{ + if (ThreadIDIsValid(thread)) + { + unsigned int info_count = THREAD_BASIC_INFO_COUNT; + kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count); + if (err == KERN_SUCCESS) + return true; + } + ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info)); + return false; +} + + +bool +ThreadMacOSX::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadMacOSX::Dump(Log *log, uint32_t index) +{ + const char * thread_run_state = NULL; + + switch (m_basic_info.run_state) + { + case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally + case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped + case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally + case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait + case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a + default: thread_run_state = "???"; break; + } + + RegisterContext *reg_context = GetRegisterContext(); + log->Printf ("thread[%u] %4.4x (%u): pc: 0x%8.8llx sp: 0x%8.8llx breakID: %d user: %d.%06.6d system: %d.%06.6d cpu: %d policy: %d run_state: %d (%s) flags: %d suspend_count: %d (current %d) sleep_time: %d", + index, + GetID (), + reg_context->GetPC (LLDB_INVALID_ADDRESS), + reg_context->GetSP (LLDB_INVALID_ADDRESS), + m_basic_info.user_time.seconds, m_basic_info.user_time.microseconds, + m_basic_info.system_time.seconds, m_basic_info.system_time.microseconds, + m_basic_info.cpu_usage, + m_basic_info.policy, + m_basic_info.run_state, + thread_run_state, + m_basic_info.flags, + m_basic_info.suspend_count, m_suspend_count, + m_basic_info.sleep_time); + //DumpRegisterState(0); +} + +void +ThreadMacOSX::ThreadWillResume (StateType resume_state) +{ + // Update the thread state to be the state we wanted when the task resumes + SetState (resume_state); + switch (resume_state) + { + case eStateSuspended: + Suspend(); + break; + + case eStateRunning: + case eStateStepping: + Resume(); + break; + } + m_context->ThreadWillResume(); +} + +void +ThreadMacOSX::DidResume () +{ + // TODO: cache current stack frames for next time in case we can match things up?? + ClearStackFrames(); + m_stop_exception.Clear(); + Thread::DidResume(); +} + +bool +ThreadMacOSX::ShouldStop(bool &step_more) +{ +// TODO: REmove this after all is working, Process should be managing this +// for us. +// +// // See if this thread is at a breakpoint? +// lldb::user_id_t breakID = CurrentBreakpoint(); +// +// if (LLDB_BREAK_ID_IS_VALID(breakID)) +// { +// // This thread is sitting at a breakpoint, ask the breakpoint +// // if we should be stopping here. +// if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID)) +// return true; +// else +// { +// // The breakpoint said we shouldn't stop, but we may have gotten +// // a signal or the user may have requested to stop in some other +// // way. Stop if we have a valid exception (this thread won't if +// // another thread was the reason this process stopped) and that +// // exception, is NOT a breakpoint exception (a common case would +// // be a SIGINT signal). +// if (GetStopException().IsValid() && !GetStopException().IsBreakpoint()) +// return true; +// } +// } +// else +// { + if (m_context->StepNotComplete()) + { + step_more = true; + return false; + } +// // The thread state is used to let us know what the thread was +// // trying to do. ThreadMacOSX::ThreadWillResume() will set the +// // thread state to various values depending if the thread was +// // the current thread and if it was to be single stepped, or +// // resumed. +// if (GetState() == eStateRunning) +// { +// // If our state is running, then we should continue as we are in +// // the process of stepping over a breakpoint. +// return false; +// } +// else +// { +// // Stop if we have any kind of valid exception for this +// // thread. +// if (GetStopException().IsValid()) +// return true; +// } +// } +// return false; + return true; +} + +bool +ThreadMacOSX::NotifyException(MachException::Data& exc) +{ + if (m_stop_exception.IsValid()) + { + // We may have more than one exception for a thread, but we need to + // only remember the one that we will say is the reason we stopped. + // We may have been single stepping and also gotten a signal exception, + // so just remember the most pertinent one. + if (m_stop_exception.IsBreakpoint()) + m_stop_exception = exc; + } + else + { + m_stop_exception = exc; + } +// bool handled = + m_context->NotifyException(exc); +// if (!handled) +// { +// handled = true; +// lldb::addr_t pc = GetPC(); +// lldb::user_id_t breakID = m_process.Breakpoints().FindIDCyAddress(pc); +// SetCurrentBreakpoint(breakID); +// switch (exc.exc_type) +// { +// case EXC_BAD_ACCESS: +// break; +// case EXC_BAD_INSTRUCTION: +// break; +// case EXC_ARITHMETIC: +// break; +// case EXC_EMULATION: +// break; +// case EXC_SOFTWARE: +// break; +// case EXC_BREAKPOINT: +// break; +// case EXC_SYSCALL: +// break; +// case EXC_MACH_SYSCALL: +// break; +// case EXC_RPC_ALERT: +// break; +// } +// } +// return handled; + return true; +} + +RegisterContext * +ThreadMacOSX::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp.reset (CreateRegisterContextForFrame (NULL)); + return m_reg_context_sp.get(); +} + +RegisterContext * +ThreadMacOSX::CreateRegisterContextForFrame (StackFrame *frame) +{ + return m_context->CreateRegisterContext (frame); +} + +uint32_t +ThreadMacOSX::SetHardwareBreakpoint (const BreakpointSite *bp) +{ + if (bp != NULL) + return GetRegisterContext()->SetHardwareBreakpoint(bp->GetLoadAddress(), bp->GetByteSize()); + return LLDB_INVALID_INDEX32; +} + +uint32_t +ThreadMacOSX::SetHardwareWatchpoint (const WatchpointLocation *wp) +{ + if (wp != NULL) + return GetRegisterContext()->SetHardwareWatchpoint(wp->GetLoadAddress(), wp->GetByteSize(), wp->WatchpointRead(), wp->WatchpointWrite()); + return LLDB_INVALID_INDEX32; +} + + +bool +ThreadMacOSX::SaveFrameZeroState (RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + checkpoint.SetStackID(frame_sp->GetStackID()); + return frame_sp->GetRegisterContext()->ReadAllRegisterValues (checkpoint.GetData()); + } + return false; +} + +bool +ThreadMacOSX::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + bool ret = frame_sp->GetRegisterContext()->WriteAllRegisterValues (checkpoint.GetData()); + + // Clear out all stack frames as our world just changed. + ClearStackFrames(); + frame_sp->GetRegisterContext()->Invalidate(); + + return ret; + } + return false; +} + +bool +ThreadMacOSX::ClearHardwareBreakpoint (const BreakpointSite *bp) +{ + if (bp != NULL && bp->IsHardware()) + return GetRegisterContext()->ClearHardwareBreakpoint(bp->GetHardwareIndex()); + return false; +} + +bool +ThreadMacOSX::ClearHardwareWatchpoint (const WatchpointLocation *wp) +{ + if (wp != NULL && wp->IsHardware()) + return GetRegisterContext()->ClearHardwareWatchpoint(wp->GetHardwareIndex()); + return false; +} + +size_t +ThreadMacOSX::GetStackFrameData(std::vector >& fp_pc_pairs) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + return m_context->GetStackFrameData(frame_sp.get(), fp_pc_pairs); +} + + +//void +//ThreadMacOSX::NotifyBreakpointChanged (const BreakpointSite *bp) +//{ +// if (bp) +// { +// lldb::user_id_t breakID = bp->GetID(); +// if (bp->IsEnabled()) +// { +// if (bp->Address() == GetPC()) +// { +// SetCurrentBreakpoint(breakID); +// } +// } +// else +// { +// if (CurrentBreakpoint() == breakID) +// { +// SetCurrentBreakpoint(LLDB_INVALID_BREAK_ID); +// } +// } +// } +//} +// + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h new file mode 100644 index 000000000000..0039f3639f15 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h @@ -0,0 +1,159 @@ +//===-- ThreadMacOSX.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadMacOSX_h_ +#define liblldb_ThreadMacOSX_h_ + +#include + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "MachException.h" + +class ProcessMacOSX; +class MachThreadContext; + +class ThreadMacOSX : public lldb_private::Thread +{ +public: + ThreadMacOSX (ProcessMacOSX &process, lldb::tid_t tid); + + virtual + ~ThreadMacOSX (); + + virtual bool + WillResume (lldb::StateType resume_state); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetInfo (); + + virtual const char * + GetName (); + + virtual lldb_private::RegisterContext * + GetRegisterContext (); + + virtual lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint); + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint); + + virtual uint32_t + GetStackFrameCount(); + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx); + + virtual void + ClearStackFrames (); + + ProcessMacOSX & + GetMacOSXProcess () + { + return (ProcessMacOSX &)m_process; + } + + const ProcessMacOSX & + GetMacOSXProcess () const + { + return (ProcessMacOSX &)m_process; + } + + void + Dump (lldb_private::Log *log, uint32_t index); + + lldb::tid_t + InferiorThreadID () const; + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + int32_t + Resume (); + + int32_t + Suspend (); + + int32_t + GetSuspendCount () const { return m_suspend_count; } + + bool + RestoreSuspendCount (); + + uint32_t + SetHardwareBreakpoint (const lldb_private::BreakpointSite *bp); + + uint32_t + SetHardwareWatchpoint (const lldb_private::WatchpointLocation *wp); + + bool + ClearHardwareBreakpoint (const lldb_private::BreakpointSite *bp); + + bool + ClearHardwareWatchpoint (const lldb_private::WatchpointLocation *wp); + + void + ThreadWillResume (lldb::StateType resume_state); + + virtual void + DidResume (); + + bool + ShouldStop (bool &step_more); + + bool + NotifyException (MachException::Data& exc); + + const MachException::Data& + GetStopException () { return m_stop_exception; } + + const char * + GetBasicInfoAsString (); + + size_t + GetStackFrameData (std::vector >& fp_pc_pairs); + + virtual bool + GetRawStopReason (lldb_private::Thread::StopInfo *stop_info); + +protected: + bool + GetIdentifierInfo (); + + const char * + GetDispatchQueueName(); + + static bool + GetBasicInfo (lldb::tid_t threadID, struct thread_basic_info *basic_info); + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::vector > m_fp_pc_pairs; + struct thread_basic_info m_basic_info; // Basic information for a thread used to see if a thread is valid + std::string m_basic_info_string;// Basic thread info as a C string. +#ifdef THREAD_IDENTIFIER_INFO_COUNT + thread_identifier_info_data_t m_ident_info; + struct proc_threadinfo m_proc_threadinfo; + std::string m_dispatch_queue_name; +#endif + int32_t m_suspend_count; // The current suspend count + MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped + std::auto_ptr m_context; // The arch specific thread context for this thread (register state and more) + +}; + +#endif // liblldb_ThreadMacOSX_h_ diff --git a/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp new file mode 100644 index 000000000000..bf6b6c2eac48 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp @@ -0,0 +1,327 @@ +//===-- LibUnwindRegisterContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LibUnwindRegisterContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" +// Project includes + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// LibUnwindRegisterContext constructor +//---------------------------------------------------------------------- +LibUnwindRegisterContext::LibUnwindRegisterContext +( + Thread &thread, + StackFrame *frame, + const lldb_private::unw_cursor_t& unwind_cursor +) : + RegisterContext (thread, frame), + m_unwind_cursor (unwind_cursor), + m_unwind_cursor_is_valid (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +LibUnwindRegisterContext::~LibUnwindRegisterContext() +{ +} + +void +LibUnwindRegisterContext::Invalidate () +{ + m_unwind_cursor_is_valid = false; +} + +size_t +LibUnwindRegisterContext::GetRegisterCount () +{ + return m_thread.GetRegisterContext()->GetRegisterCount(); +} + +const lldb::RegisterInfo * +LibUnwindRegisterContext::GetRegisterInfoAtIndex (uint32_t reg) +{ + return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); +} + +size_t +LibUnwindRegisterContext::GetRegisterSetCount () +{ + return m_thread.GetRegisterContext()->GetRegisterSetCount(); +} + + + +const lldb::RegisterSet * +LibUnwindRegisterContext::GetRegisterSet (uint32_t reg_set) +{ + return m_thread.GetRegisterContext()->GetRegisterSet (reg_set); +} + + + +bool +LibUnwindRegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + if (m_unwind_cursor_is_valid == false) + return false; + + // Read the register + unw_word_t reg_value; + if (unw_get_reg (&m_unwind_cursor, reg, ®_value) != UNW_ESUCCESS) + return false; + + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + switch (reg_info->encoding) + { + case eEncodingUint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (uint32_t)reg_value; + return true; + + case 8: + value = (uint64_t)reg_value; + return true; + } + break; + + case eEncodingSint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (int32_t)reg_value; + return true; + + case 8: + value = (int64_t)reg_value; + return true; + } + break; + + case eEncodingIEEE754: + if (reg_info->byte_size > sizeof(unw_word_t)) + return false; + + switch (reg_info->byte_size) + { + case sizeof (float): + if (sizeof (float) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (float) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (double): + if (sizeof (double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (long double): + if (sizeof (long double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (long double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + } + break; + } + return false; +} + + +bool +LibUnwindRegisterContext::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + Scalar reg_value; + + if (ReadRegisterValue (reg, reg_value)) + { + if (reg_value.GetData(data)) + { + // "reg_value" is local and now "data" points to the data within + // "reg_value", so we must make a copy that will live within "data" + DataBufferSP data_sp (new DataBufferHeap (data.GetDataStart(), data.GetByteSize())); + data.SetData (data_sp, 0, data.GetByteSize()); + return true; + } + } + return false; +} + + +bool +LibUnwindRegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + unw_word_t reg_value; + switch (value.GetType()) + { + case Scalar::e_sint: reg_value = value.SInt(); break; + case Scalar::e_uint: reg_value = value.UInt(); break; + case Scalar::e_slong: reg_value = value.SLong(); break; + case Scalar::e_ulong: reg_value = value.ULong(); break; + case Scalar::e_slonglong: reg_value = value.SLongLong(); break; + case Scalar::e_ulonglong: reg_value = value.ULongLong(); break; + case Scalar::e_float: + if (sizeof (float) == sizeof (unsigned int)) + reg_value = value.UInt(); + else if (sizeof (float) == sizeof (unsigned long)) + reg_value = value.ULong(); + else if (sizeof (float) == sizeof (unsigned long long)) + reg_value = value.ULongLong(); + else + return false; + break; + + case Scalar::e_double: + if (sizeof (double) == sizeof (unsigned int)) + reg_value = value.UInt(); + else if (sizeof (double) == sizeof (unsigned long)) + reg_value = value.ULong(); + else if (sizeof (double) == sizeof (unsigned long long)) + reg_value = value.ULongLong(); + else + return false; + break; + + case Scalar::e_long_double: + if (sizeof (long double) == sizeof (unsigned int)) + reg_value = value.UInt(); + else if (sizeof (long double) == sizeof (unsigned long)) + reg_value = value.ULong(); + else if (sizeof (long double) == sizeof (unsigned long long)) + reg_value = value.ULongLong(); + else + return false; + break; + } + + return unw_set_reg (&m_unwind_cursor, reg, reg_value) == UNW_ESUCCESS; +} + + +bool +LibUnwindRegisterContext::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + + if (reg_info == NULL) + return false; + if (reg_info->byte_size > sizeof (unw_word_t)) + return false; + + Scalar value; + uint32_t offset = data_offset; + + switch (reg_info->encoding) + { + case eEncodingUint: + if (reg_info->byte_size <= 4) + value = data.GetMaxU32 (&offset, reg_info->byte_size); + else if (reg_info->byte_size <= 8) + value = data.GetMaxU64 (&offset, reg_info->byte_size); + else + return false; + break; + + case eEncodingSint: + if (reg_info->byte_size <= 4) + value = (int32_t)data.GetMaxU32 (&offset, reg_info->byte_size); + else if (reg_info->byte_size <= 8) + value = data.GetMaxS64 (&offset, reg_info->byte_size); + else + return false; + break; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + value = data.GetFloat (&offset); + break; + + case sizeof (double): + value = data.GetDouble (&offset); + break; + + case sizeof (long double): + value = data.GetLongDouble (&offset); + break; + default: + return false; + } + } + return WriteRegisterValue (reg, value); +} + + +bool +LibUnwindRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + // libunwind frames can't handle this it doesn't always have all register + // values. This call should only be called on frame zero anyway so there + // shouldn't be any problem + return false; +} + +bool +LibUnwindRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + // Since this class doesn't respond to "ReadAllRegisterValues()", it must + // not have been the one that saved all the register values. So we just let + // the thread's register context (the register context for frame zero) do + // the writing. + return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp); +} + + +uint32_t +LibUnwindRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num); +} + diff --git a/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h new file mode 100644 index 000000000000..4e89b27961f6 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h @@ -0,0 +1,83 @@ +//===-- LibUnwindRegisterContext.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_LibUnwindRegisterContext_h_ +#define lldb_LibUnwindRegisterContext_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +#include "libunwind.h" + +class LibUnwindRegisterContext : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + LibUnwindRegisterContext (lldb_private::Thread &thread, + lldb_private::StackFrame *frame, + const lldb_private::unw_cursor_t &unwind_cursor); + + virtual + ~LibUnwindRegisterContext (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t reg_set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +private: + lldb_private::unw_cursor_t m_unwind_cursor; + bool m_unwind_cursor_is_valid; + //------------------------------------------------------------------ + // For LibUnwindRegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (LibUnwindRegisterContext); +}; + +#endif // lldb_LibUnwindRegisterContext_h_ diff --git a/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp new file mode 100644 index 000000000000..58a7ac043c5d --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp @@ -0,0 +1,306 @@ +//===-- MacOSXLibunwindCallbacks.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MacOSXLibunwindCallbacks_cpp_ +#define liblldb_MacOSXLibunwindCallbacks_cpp_ +#if defined(__cplusplus) + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "lldb-enumerations.h" +#include "libunwind.h" +#include "llvm-c/EnhancedDisassembly.h" + +using namespace lldb; + +namespace lldb_private { + +/* Don't implement (libunwind does not use) + find_proc_info + put_unwind_info + get_dyn_info_list_addr + access_mem + resume +*/ +/* + Should implement (not needed yet) + access_fpreg + access_vecreg + proc_is_sigtramp + proc_is_inferior_function_call + access_reg_inf_func_call +*/ + +static int +access_reg (lldb_private::unw_addr_space_t as, lldb_private::unw_regnum_t regnum, lldb_private::unw_word_t *valp, int write, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + /* FIXME Only support reading for now. */ + if (write == 1) + return -1; + if (th->GetRegisterContext()->GetRegisterInfoAtIndex(regnum) == NULL) + return -1; + DataExtractor de; + if (!th->GetRegisterContext()->ReadRegisterBytes (regnum, de)) + return -1; + memcpy (valp, de.GetDataStart(), de.GetByteSize()); + return UNW_ESUCCESS; +} + +static int +get_proc_name (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t ip, char *bufp, size_t buf_len, lldb_private::unw_word_t *offp, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + Address addr; + if (!th->GetProcess().ResolveLoadAddress(ip, addr)) + return -1; + + SymbolContext sc; + if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextFunction, sc)) + return -1; + if (!sc.symbol) + return -1; + strlcpy (bufp, sc.symbol->GetMangled().GetMangledName().AsCString(""), buf_len); + if (offp) + *offp = addr.GetLoadAddress(&th->GetProcess()) - sc.symbol->GetValue().GetLoadAddress(&th->GetProcess()); + return UNW_ESUCCESS; +} + +static int +find_image_info (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t load_addr, lldb_private::unw_word_t *mh, + lldb_private::unw_word_t *text_start, lldb_private::unw_word_t *text_end, + lldb_private::unw_word_t *eh_frame, lldb_private::unw_word_t *eh_frame_len, + lldb_private::unw_word_t *compact_unwind_start, lldb_private::unw_word_t *compact_unwind_len, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + Address addr; + if (!th->GetProcess().ResolveLoadAddress(load_addr, addr)) + return -1; + + SymbolContext sc; + if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextModule, sc)) + return -1; + + SectionList *sl = sc.module_sp->GetObjectFile()->GetSectionList(); + static ConstString g_segment_name_TEXT("__TEXT"); + SectionSP text_segment_sp(sl->FindSectionByName(g_segment_name_TEXT)); + if (!text_segment_sp) + return -1; + + *mh = text_segment_sp->GetLoadBaseAddress (&th->GetProcess()); + *text_start = text_segment_sp->GetLoadBaseAddress (&th->GetProcess()); + *text_end = *text_start + text_segment_sp->GetByteSize(); + + static ConstString g_section_name_eh_frame ("__eh_frame"); + SectionSP eh_frame_section_sp = text_segment_sp->GetChildren().FindSectionByName(g_section_name_eh_frame); + if (eh_frame_section_sp.get()) { + *eh_frame = eh_frame_section_sp->GetLoadBaseAddress (&th->GetProcess()); + *eh_frame_len = eh_frame_section_sp->GetByteSize(); + } else { + *eh_frame = 0; + *eh_frame_len = 0; + } + + static ConstString g_section_name_unwind_info ("__unwind_info"); + SectionSP unwind_info_section_sp = text_segment_sp->GetChildren().FindSectionByName(g_section_name_unwind_info); + if (unwind_info_section_sp.get()) { + *compact_unwind_start = unwind_info_section_sp->GetLoadBaseAddress (&th->GetProcess()); + *compact_unwind_len = unwind_info_section_sp->GetByteSize(); + } else { + *compact_unwind_start = 0; + *compact_unwind_len = 0; + } + return UNW_ESUCCESS; +} + +static int +get_proc_bounds (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t ip, lldb_private::unw_word_t *low, lldb_private::unw_word_t *high, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + Address addr; + if (!th->GetProcess().ResolveLoadAddress(ip, addr)) + return -1; + SymbolContext sc; + if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextFunction | eSymbolContextSymbol, sc)) + return -1; + if (sc.function) + { + lldb::addr_t start, len; + start = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(&th->GetProcess()); + len = sc.function->GetAddressRange().GetByteSize(); + if (start == LLDB_INVALID_ADDRESS || len == LLDB_INVALID_ADDRESS) + return -1; + *low = start; + *high = start + len; + return UNW_ESUCCESS; + } + if (sc.symbol) + { + lldb::addr_t start, len; + start = sc.symbol->GetAddressRangeRef().GetBaseAddress().GetLoadAddress(&th->GetProcess()); + len = sc.symbol->GetAddressRangeRef().GetByteSize(); + if (start == LLDB_INVALID_ADDRESS) + return -1; + *low = start; + if (len != LLDB_INVALID_ADDRESS) + *high = start + len; + else + *high = 0; + return UNW_ESUCCESS; + } + return -1; +} + +static int +access_raw (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t addr, lldb_private::unw_word_t extent, uint8_t *valp, int write, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + /* FIXME Only support reading for now. */ + if (write == 1) + return -1; + + Error error; + if (th->GetProcess().ReadMemory (addr, valp, extent, error) != extent) + return -1; + return UNW_ESUCCESS; +} + + +static int +reg_info (lldb_private::unw_addr_space_t as, lldb_private::unw_regnum_t regnum, lldb_private::unw_regtype_t *type, char *buf, size_t buflen, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + RegisterContext *regc = th->GetRegisterContext(); + if (regnum > regc->GetRegisterCount()) + { + *type = UNW_NOT_A_REG; + return UNW_ESUCCESS; + } + + const char *name = regc->GetRegisterName (regnum); + if (name == NULL) + { + *type = UNW_NOT_A_REG; + return UNW_ESUCCESS; + } + strlcpy (buf, name, buflen); + + const lldb::RegisterInfo *reginfo = regc->GetRegisterInfoAtIndex (regnum); + if (reginfo == NULL || reginfo->encoding == eEncodingInvalid) + { + *type = UNW_NOT_A_REG; + return UNW_ESUCCESS; + } + if (reginfo->encoding == eEncodingUint || reginfo->encoding == eEncodingSint) + *type = UNW_INTEGER_REG; + if (reginfo->encoding == eEncodingIEEE754) + *type = UNW_FLOATING_POINT_REG; + if (reginfo->encoding == eEncodingVector) + *type = UNW_VECTOR_REG; + + return UNW_ESUCCESS; +} + + +static int +read_byte_for_edis (uint8_t *buf, uint64_t addr, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + DataBufferHeap onebyte(1, 0); + Error error; + if (th->GetProcess().ReadMemory (addr, onebyte.GetBytes(), onebyte.GetByteSize(), error) != 1) + return -1; + *buf = onebyte.GetBytes()[0]; + return UNW_ESUCCESS; +} + +static int +instruction_length (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t addr, int *length, void *arg) +{ + EDDisassemblerRef disasm; + EDInstRef cur_insn; + + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + const ArchSpec target_arch (th->GetProcess().GetTarget().GetArchitecture ()); + + if (target_arch.GetCPUType() == CPU_TYPE_I386) + { + if (EDGetDisassembler (&disasm, "i386-apple-darwin", kEDAssemblySyntaxX86ATT) != 0) + return -1; + } + else if (target_arch.GetCPUType() == CPU_TYPE_X86_64) + { + if (EDGetDisassembler (&disasm, "x86_64-apple-darwin", kEDAssemblySyntaxX86ATT) != 0) + return -1; + } + else + { + return -1; + } + + if (EDCreateInsts (&cur_insn, 1, disasm, read_byte_for_edis, addr, arg) != 1) + return -1; + *length = EDInstByteSize (cur_insn); + EDReleaseInst (cur_insn); + return UNW_ESUCCESS; +} + +lldb_private::unw_accessors_t +get_macosx_libunwind_callbacks () { + lldb_private::unw_accessors_t ap; + bzero (&ap, sizeof (lldb_private::unw_accessors_t)); + ap.find_proc_info = NULL; + ap.put_unwind_info = NULL; + ap.get_dyn_info_list_addr = NULL; + ap.find_image_info = find_image_info; + ap.access_mem = NULL; + ap.access_reg = access_reg; + ap.access_fpreg = NULL; + ap.access_vecreg = NULL; + ap.resume = NULL; + ap.get_proc_name = get_proc_name; + ap.get_proc_bounds = get_proc_bounds; + ap.access_raw = access_raw; + ap.reg_info = reg_info; + ap.proc_is_sigtramp = NULL; + ap.proc_is_inferior_function_call = NULL; + ap.access_reg_inf_func_call = NULL; + ap.instruction_length = instruction_length; + return ap; +} + + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_MacOSXLibunwindCallbacks_cpp_ diff --git a/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h new file mode 100644 index 000000000000..78bd27b2ad30 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h @@ -0,0 +1,22 @@ +//===-- MacOSXLibunwindCallbacks.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MacOSXLibunwindCallbacks_h_ +#define liblldb_MacOSXLibunwindCallbacks_h_ +#if defined(__cplusplus) + +namespace lldb_private { + +unw_accessors_t get_macosx_libunwind_callbacks (); + +} // namespace lldb_utility + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_MacOSXLibunwindCallbacks_h_ + diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp new file mode 100644 index 000000000000..df2f7c07f658 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp @@ -0,0 +1,255 @@ +//===-- RegisterContextMacOSXFrameBackchain.cpp -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMacOSXFrameBackchain.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Thread.h" +// Project includes +#include "StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RegisterContextMacOSXFrameBackchain constructor +//---------------------------------------------------------------------- +RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain +( + Thread &thread, + StackFrame *frame, + const UnwindMacOSXFrameBackchain::Cursor &cursor +) : + RegisterContext (thread, frame), + m_cursor (cursor), + m_cursor_is_valid (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain() +{ +} + +void +RegisterContextMacOSXFrameBackchain::Invalidate () +{ + m_cursor_is_valid = false; +} + +size_t +RegisterContextMacOSXFrameBackchain::GetRegisterCount () +{ + return m_thread.GetRegisterContext()->GetRegisterCount(); +} + +const lldb::RegisterInfo * +RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex (uint32_t reg) +{ + return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); +} + +size_t +RegisterContextMacOSXFrameBackchain::GetRegisterSetCount () +{ + return m_thread.GetRegisterContext()->GetRegisterSetCount(); +} + + + +const lldb::RegisterSet * +RegisterContextMacOSXFrameBackchain::GetRegisterSet (uint32_t reg_set) +{ + return m_thread.GetRegisterContext()->GetRegisterSet (reg_set); +} + + + +bool +RegisterContextMacOSXFrameBackchain::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + if (!m_cursor_is_valid) + return false; + + uint64_t reg_value = LLDB_INVALID_ADDRESS; + + const RegisterInfo *reg_info = m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg_info->kinds[eRegisterKindGeneric]) + { + case LLDB_REGNUM_GENERIC_PC: + if (m_cursor.pc == LLDB_INVALID_ADDRESS) + return false; + reg_value = m_cursor.pc; + break; + + case LLDB_REGNUM_GENERIC_FP: + if (m_cursor.fp == LLDB_INVALID_ADDRESS) + return false; + reg_value = m_cursor.pc; + break; + + default: + return false; + } + + switch (reg_info->encoding) + { + case eEncodingUint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (uint32_t)reg_value; + return true; + + case 8: + value = (uint64_t)reg_value; + return true; + } + break; + + case eEncodingSint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (int32_t)reg_value; + return true; + + case 8: + value = (int64_t)reg_value; + return true; + } + break; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + if (sizeof (float) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (float) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (double): + if (sizeof (double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (long double): + if (sizeof (long double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (long double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + } + break; + } + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + Scalar reg_value; + + if (ReadRegisterValue (reg, reg_value)) + { + if (reg_value.GetData(data)) + { + // "reg_value" is local and now "data" points to the data within + // "reg_value", so we must make a copy that will live within "data" + DataBufferSP data_sp (new DataBufferHeap (data.GetDataStart(), data.GetByteSize())); + data.SetData (data_sp, 0, data.GetByteSize()); + return true; + } + } + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + // Not supported yet. We could easily add support for this by remembering + // the address of each entry (it would need to be part of the cursor) + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + // Not supported yet. We could easily add support for this by remembering + // the address of each entry (it would need to be part of the cursor) + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + // libunwind frames can't handle this it doesn't always have all register + // values. This call should only be called on frame zero anyway so there + // shouldn't be any problem + return false; +} + +bool +RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + // Since this class doesn't respond to "ReadAllRegisterValues()", it must + // not have been the one that saved all the register values. So we just let + // the thread's register context (the register context for frame zero) do + // the writing. + return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp); +} + + +uint32_t +RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num); +} + diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h new file mode 100644 index 000000000000..f4118c2795df --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h @@ -0,0 +1,83 @@ +//===-- RegisterContextMacOSXFrameBackchain.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_RegisterContextMacOSXFrameBackchain_h_ +#define lldb_RegisterContextMacOSXFrameBackchain_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +#include "UnwindMacOSXFrameBackchain.h" + +class RegisterContextMacOSXFrameBackchain : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextMacOSXFrameBackchain (lldb_private::Thread &thread, + lldb_private::StackFrame *frame, + const UnwindMacOSXFrameBackchain::Cursor &cursor); + + virtual + ~RegisterContextMacOSXFrameBackchain (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t reg_set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +private: + UnwindMacOSXFrameBackchain::Cursor m_cursor; + bool m_cursor_is_valid; + //------------------------------------------------------------------ + // For RegisterContextMacOSXFrameBackchain only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContextMacOSXFrameBackchain); +}; + +#endif // lldb_RegisterContextMacOSXFrameBackchain_h_ diff --git a/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp new file mode 100644 index 000000000000..1b6fa58dcd48 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp @@ -0,0 +1,73 @@ +//===-- UnwindLibUnwind.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/Target/Thread.h" +#include "UnwindLibUnwind.h" +#include "LibUnwindRegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindLibUnwind::UnwindLibUnwind (Thread &thread, unw_addr_space_t addr_space) : + Unwind (thread), + m_addr_space (addr_space), + m_cursors() +{ + m_pc_regnum = thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + m_sp_regnum = thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); +} + +uint32_t +UnwindLibUnwind::GetFrameCount() +{ + if (m_cursors.empty()) + { + unw_cursor_t cursor; + unw_init_remote (&cursor, m_addr_space, &m_thread); + + m_cursors.push_back (cursor); + + while (1) + { + int stepresult = unw_step (&cursor); + if (stepresult > 0) + m_cursors.push_back (cursor); + else + break; + } + } + return m_cursors.size(); +} + +bool +UnwindLibUnwind::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc) +{ + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + { + int pc_err = unw_get_reg (&m_cursors[idx], m_pc_regnum, &pc); + int sp_err = unw_get_reg (&m_cursors[idx], m_sp_regnum, &cfa); + return pc_err == UNW_ESUCCESS && sp_err == UNW_ESUCCESS; + } + return false; +} + +RegisterContext * +UnwindLibUnwind::CreateRegisterContextForFrame (StackFrame *frame) +{ + uint32_t idx = frame->GetID(); + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + return new LibUnwindRegisterContext (m_thread, frame, m_cursors[idx]); + return NULL; +} diff --git a/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h new file mode 100644 index 000000000000..d91f164a2f9d --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h @@ -0,0 +1,66 @@ +//===-- UnwindLibUnwind.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_UnwindLibUnwind_h_ +#define lldb_UnwindLibUnwind_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "libunwind.h" + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Unwind.h" + +class UnwindLibUnwind : public lldb_private::Unwind +{ +public: + UnwindLibUnwind (lldb_private::Thread &thread, + lldb_private::unw_addr_space_t addr_space); + + virtual + ~UnwindLibUnwind() + { + } + + virtual void + Clear() + { + m_cursors.clear(); + } + + virtual uint32_t + GetFrameCount(); + + bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc); + + lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + lldb_private::Thread & + GetThread(); + +private: + lldb_private::unw_addr_space_t m_addr_space; + std::vector m_cursors; + uint32_t m_pc_regnum; + uint32_t m_sp_regnum; + //------------------------------------------------------------------ + // For UnwindLibUnwind only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (UnwindLibUnwind); +}; + +#endif // lldb_UnwindLibUnwind_h_ diff --git a/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp new file mode 100644 index 000000000000..586e3d788645 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp @@ -0,0 +1,243 @@ +//===-- UnwindMacOSXFrameBackchain.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/ArchSpec.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "RegisterContextMacOSXFrameBackchain.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain (Thread &thread) : + Unwind (thread), + m_cursors() +{ +} + +uint32_t +UnwindMacOSXFrameBackchain::GetFrameCount() +{ + if (m_cursors.empty()) + { + const ArchSpec target_arch (m_thread.GetProcess().GetTarget().GetArchitecture ()); + // Frame zero should always be supplied by the thread... + StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0)); + if (target_arch == ArchSpec("x86_64")) + GetStackFrameData_x86_64 (frame_sp.get()); + else if (target_arch == ArchSpec("i386")) + GetStackFrameData_i386 (frame_sp.get()); + + } + return m_cursors.size(); +} + +bool +UnwindMacOSXFrameBackchain::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc) +{ + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + { + if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS) + return false; + if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS) + return false; + + pc = m_cursors[idx].pc; + cfa = m_cursors[idx].fp; + + return true; + } + return false; +} + +RegisterContext * +UnwindMacOSXFrameBackchain::CreateRegisterContextForFrame (StackFrame *frame) +{ + uint32_t idx = frame->GetID(); + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + return new RegisterContextMacOSXFrameBackchain (m_thread, frame, m_cursors[idx]); + return NULL; +} + +size_t +UnwindMacOSXFrameBackchain::GetStackFrameData_i386 (StackFrame *first_frame) +{ + m_cursors.clear(); + + std::pair fp_pc_pair; + + typedef struct Frame_i386 + { + uint32_t fp; + uint32_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + + Cursor cursor; + cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); + cursor.fp = reg_ctx->GetFP (0); + + Frame_i386 frame = { cursor.fp, cursor.pc }; + + m_cursors.push_back(cursor); + + const size_t k_frame_size = sizeof(frame); + Error error; + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (8 bytes) + if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + if (frame.pc >= 0x1000) + { + cursor.pc = frame.pc; + cursor.fp = frame.fp; + m_cursors.push_back (cursor); + } + } + if (!m_cursors.empty()) + { + lldb::addr_t first_frame_pc = m_cursors.front().pc; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc (first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr(); + + if (addr_range_ptr) + { + if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); + // Read the real second frame return address into frame.pc + if (first_frame_sp && m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + cursor.fp = m_cursors.front().fp; + cursor.pc = frame.pc; // Set the new second frame PC + + // Insert the second frame + m_cursors.insert(m_cursors.begin()+1, cursor); + + m_cursors.front().fp = first_frame_sp; + } + } + } + } + } +// uint32_t i=0; +// printf(" PC FP\n"); +// printf(" ------------------ ------------------ \n"); +// for (i=0; i fp_pc_pair; + + typedef struct Frame_x86_64 + { + uint64_t fp; + uint64_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + + Cursor cursor; + cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); + cursor.fp = reg_ctx->GetFP (0); + + Frame_x86_64 frame = { cursor.fp, cursor.pc }; + + m_cursors.push_back(cursor); + Error error; + const size_t k_frame_size = sizeof(frame); + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (16 bytes) + if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + + if (frame.pc >= 0x1000) + { + cursor.pc = frame.pc; + cursor.fp = frame.fp; + m_cursors.push_back (cursor); + } + } + if (!m_cursors.empty()) + { + lldb::addr_t first_frame_pc = m_cursors.front().pc; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr(); + + if (addr_range_ptr) + { + if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); + // Read the real second frame return address into frame.pc + if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + cursor.fp = m_cursors.front().fp; + cursor.pc = frame.pc; // Set the new second frame PC + + // Insert the second frame + m_cursors.insert(m_cursors.begin()+1, cursor); + + m_cursors.front().fp = first_frame_sp; + } + } + } + } + } + return m_cursors.size(); +} + diff --git a/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h new file mode 100644 index 000000000000..86ba6e7ae7f7 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h @@ -0,0 +1,77 @@ +//===-- UnwindMacOSXFrameBackchain.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_UnwindMacOSXFrameBackchain_h_ +#define lldb_UnwindMacOSXFrameBackchain_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Unwind.h" + +class UnwindMacOSXFrameBackchain : public lldb_private::Unwind +{ +public: + UnwindMacOSXFrameBackchain (lldb_private::Thread &thread); + + virtual + ~UnwindMacOSXFrameBackchain() + { + } + + virtual void + Clear() + { + m_cursors.clear(); + } + + virtual uint32_t + GetFrameCount(); + + bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc); + + lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + lldb_private::Thread & + GetThread(); + +protected: + friend class RegisterContextMacOSXFrameBackchain; + + typedef struct Cursor + { + lldb::addr_t pc; // Program counter + lldb::addr_t fp; // Frame pointer for us with backchain + }; + +private: + std::vector m_cursors; + + size_t + GetStackFrameData_i386 (lldb_private::StackFrame *first_frame); + + size_t + GetStackFrameData_x86_64 (lldb_private::StackFrame *first_frame); + + //------------------------------------------------------------------ + // For UnwindMacOSXFrameBackchain only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (UnwindMacOSXFrameBackchain); +}; + +#endif // lldb_UnwindMacOSXFrameBackchain_h_ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h b/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h new file mode 100644 index 000000000000..63cc8ba23660 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h @@ -0,0 +1,509 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- libunwind.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// C interface to libuwind +// +// Source compatible with Level 1 Base ABI documented at: +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + + +#ifndef __LIBUNWIND__ +#define __LIBUNWIND__ + +#include +#include +#include +#include +#include + +namespace lldb_private { + +#pragma mark Error codes + +enum { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC = -6540, /* unspecified (general) error */ + UNW_ENOMEM = -6541, /* out of memory */ + UNW_EBADREG = -6542, /* bad register number */ + UNW_EREADONLYREG = -6543, /* attempt to write read-only register */ + UNW_ESTOPUNWIND = -6544, /* stop unwinding */ + UNW_EINVALIDIP = -6545, /* invalid IP */ + UNW_EBADFRAME = -6546, /* bad frame */ + UNW_EINVAL = -6547, /* unsupported operation or bad value */ + UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ + UNW_ENOINFO = -6549, /* no unwind info found */ + UNW_EREGUNAVAILABLE = -6550 /* contents of requested reg are not available */ +}; + +#pragma mark General data structures + +struct unw_context_t { uint64_t data[128]; }; +typedef struct unw_context_t unw_context_t; + +struct unw_cursor_t { uint64_t data[140]; }; +typedef struct unw_cursor_t unw_cursor_t; + +enum unw_as_type { UNW_LOCAL, UNW_REMOTE }; +struct unw_addr_space +{ + enum unw_as_type type; + uint8_t data[]; +}; +typedef struct unw_addr_space* unw_addr_space_t; + +typedef int unw_regnum_t; +typedef uint64_t unw_word_t; +typedef double unw_fpreg_t; + +enum unw_vecreg_format { + UNW_VECREG_SIGNED, + UNW_VECREG_UNSIGNED, + UNW_VECREG_FLOAT +}; + +typedef struct +{ + union { + double doubles[8]; + float floats [16]; + + uint64_t dwords [8]; + uint32_t words [16]; + uint16_t hwords [32]; + uint8_t bytes [64]; + } data; + uint16_t unit_size; // bits + uint16_t num_units; + uint8_t format; +} unw_vecreg_t; + +struct unw_proc_info_t +{ + unw_word_t start_ip; /* start address of function */ + unw_word_t end_ip; /* address after end of function */ + unw_word_t lsda; /* address of language specific data area, or zero if not used */ + unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t gp; /* not used */ + unw_word_t flags; /* not used */ + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of dwarf unwind info, or zero if none */ + unw_word_t unwind_info; /* address of dwarf unwind info, or zero if none */ + unw_word_t extra; /* mach_header of mach-o image containing function */ +}; +typedef struct unw_proc_info_t unw_proc_info_t; + +#pragma mark Local API + +#ifdef __cplusplus +extern "C" { +#endif + +extern int unw_getcontext(unw_context_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_init_local(unw_cursor_t*, unw_context_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_step(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_reg(unw_cursor_t*, unw_regnum_t, unw_word_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_fpreg(unw_cursor_t*, unw_regnum_t, unw_fpreg_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_set_reg(unw_cursor_t*, unw_regnum_t, unw_word_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_set_fpreg(unw_cursor_t*, unw_regnum_t, unw_fpreg_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_resume(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +extern const char* unw_regname(unw_cursor_t*, unw_regnum_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_proc_info(unw_cursor_t*, unw_proc_info_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_is_fpreg(unw_cursor_t*, unw_regnum_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_is_signal_frame(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_proc_name(unw_cursor_t*, char*, size_t, unw_word_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*); + + +#pragma mark Remote data structures + +typedef enum { + UNW_NOT_A_REG = 0, + UNW_INTEGER_REG, + UNW_FLOATING_POINT_REG, + UNW_VECTOR_REG, + UNW_OTHER_REG +} unw_regtype_t; + +typedef enum { + UNW_TARGET_UNSPECIFIED = 0, + UNW_TARGET_I386, + UNW_TARGET_X86_64, + UNW_TARGET_PPC, + UNW_TARGET_ARM +} unw_targettype_t; + +typedef enum { + UNW_LOG_LEVEL_NONE = 0x00000000, + UNW_LOG_LEVEL_INFO = 0x00000001, + UNW_LOG_LEVEL_API = 0x00000002, + UNW_LOG_LEVEL_VERBOSE = 0x00000004, + UNW_LOG_LEVEL_TIMINGS = 0x00000008, + UNW_LOG_LEVEL_DEBUG = 0x00000010, + UNW_LOG_LEVEL_ALL = 0x0FFFFFFF +} unw_log_level_t; + +typedef enum { + UNW_CACHE_NONE = 0, + UNW_CACHE_GLOBAL, + UNW_CACHE_PER_THREAD +} unw_caching_policy_t; + +typedef struct { + int (*find_proc_info)(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int need_unwind_info, void *arg); + int (*put_unwind_info)(unw_addr_space_t as, unw_proc_info_t *pip, void *arg); + int (*get_dyn_info_list_addr)(unw_addr_space_t as, unw_word_t *dilap, void *arg); + + // Reads or writes a memory object the size of a target pointer. + // Byte-swaps if necessary. + int (*access_mem)(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg); + + // Register contents sent as-is (i.e. not byte-swapped). + // Register numbers are the driver program's numbering scheme as + // determined by the reg_info callbacks; libunwind will interrogate + // the driver program to figure out which numbers it uses to refer to + // which registers. + int (*access_reg)(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg); + int (*access_fpreg)(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *valp, int write, void *arg); + int (*resume)(unw_addr_space_t as, unw_cursor_t *cp, void *arg); + int (*get_proc_name)(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg); + + + // Added to find the start of the image (executable, bundle, dylib, etc) + // for a given address. + // as - The address space to use + // ip - The address libunwind wants to know about + // mh - The Mach-O header address for this image + // text_start - The start of __TEXT segment (all its sections) + // text_end - The end address of __TEXT segment (all its sections) + // eh_frame - The start of __TEXT,__eh_frame + // eh_frame_len - The length of __TEXT,__eh_frame + // compact_unwind_start - The start of __TEXT,__unwind_info + // compact_unwind_len - The length of __TEXT,__unwind_info + // arg - The driver-provided generic argument + // All addresses are the in-memory, slid, addresses. + // If eh_frame or unwind_info are missing, addr and len is returned as 0. + int (*find_image_info)(unw_addr_space_t as, unw_word_t ip, unw_word_t *mh, + unw_word_t *text_start, unw_word_t *text_end, + unw_word_t *eh_frame, unw_word_t *eh_frame_len, + unw_word_t *compact_unwind_start, + unw_word_t *compact_unwind_len, void *arg); + + // Added to get the start and end address of a function without needing + // all of the information (and potential allocation) that the + // find_proc_info() call entails. + // as - The address space to use + // ip - The address libunwind wants to know about + // low - The start address of the function at 'ip' + // high - The first address past the function at 'ip' + // arg - The driver-provided generic argument + // If HIGH is unknown, it should be set to 0. All addresses + // are the in-memory, slid, addresses. + int (*get_proc_bounds)(unw_addr_space_t as, unw_word_t ip, + unw_word_t *low, unw_word_t *high, void *arg); + + // Added to support accessing non-word-size memory objects across + // platforms. No byte swapping should be done. + // as - The address space to use + // addr - The starting address to access + // extent - The extent of the region to access, in bytes + // valp - The local region to be written from / read into + // write - non-zero if the data is to be written into the target + // rather than read + // arg - The driver-provided generic argument (see unw_init_remote) + int (*access_raw)(unw_addr_space_t as, unw_word_t addr, unw_word_t extent, + uint8_t *valp, int write, void *arg); + + // Added to support identifying registers. + // libunwind will interrogate the driver program via this callback to + // identify what register numbers it is using; the register names are + // used to correlate that the driver program's register numbers with + // libunwind's internal register numbers. The driver program should + // use its own register numbers when requesting registers with + // unw_get_reg() and libunwind will provide the driver program's + // register numbers to the access_reg callback function. + // as - The address space to use + // regnum - The register number + // type - Write the register type to this address + // For a non-existent register, return UNW_ESUCCESS but + // write UNW_NOT_A_REG to type + // buf - If non-NULL, the register name is written to this address + // buf_len - The size of the buffer provided for the register name + // arg - The driver-provided generic argument (see unw_init_remote) + int (*reg_info)(unw_addr_space_t as, unw_regnum_t regnum, + unw_regtype_t* type, char *bufp, size_t buf_len, void *arg); + + // Added to read a vector register's value from the remote machine. + // as - The address space to use + // regnum - The register number + // valp - The local region to be written from / read into + // write - non-zero if the data is to be written into the target + // rather than read + // arg - The driver-specified generic argument + int (*access_vecreg)(unw_addr_space_t as, unw_regnum_t regnum, + unw_vecreg_t* valp, int write, void *arg); + + // Added to identify if an unwind cursor is pointing to _sigtramp(). + // After a _sigtramp we have an entire register set available and we should + // return any of the registers requested. + // as - The address space to use + // ip - The address of the function libunwind is examining + // arg - The driver-provided generic argument + // This function returns non-zero if ip is in _sigtramp. + int (*proc_is_sigtramp) (unw_addr_space_t as, unw_word_t ip, void *arg); + + // Added to identify if an unwind cursor is pointing to a debugger's + // inferior function call dummy frame. + // The driver program will need to provide the full register set (via the + // standard access_reg callback) for the function that was executing + // when the inferior function call was made; it will use these register + // values and not try to unwind out of the inferior function call dummy + // frame. + // After a inf func call we have an entire register set available and + // we should return any of the registers requested. + // as - The address space to use + // ip - The address of the function libunwind is examining + // sp - The stack pointer value of the frame + // arg - The driver-provided generic argument (see unw_init_remote) + // This function returns non-zero if ip/sp is an inferior function call + // dummy frame. + int (*proc_is_inferior_function_call) (unw_addr_space_t as, unw_word_t ip, + unw_word_t sp, void *arg); + + // Added to retrieve a register value from a above a debugger's inferior + // function call dummy frame. Similar to _sigtramp but the debugger will + // have the register context squirreled away in its own memory (or possibly + // saved on the stack somewhere). + // May be NULL if the program being unwound will not have a debugger + // calling functions mid-execution. + // as - The address space to use + // ip - The pc value for the dummy frame + // sp - The stack pointer for the dummy frame + // regnum - The register number in the driver program's register + // numbering scheme. + // valp - Pointer to a word of memory to be read/written + // write - Non-zero if libunwind is writing a new value to the reg, + // else it is reading the contents of that register. + // arg - The driver-provided generic argument (see unw_init_remote) + int (*access_reg_inf_func_call)(unw_addr_space_t as, unw_word_t ip, + unw_word_t sp, unw_regnum_t regnum, + unw_word_t *valp, int write, void *arg); + + // Added to iterate over unknown assembly instructions when analyzing a + // function prologue. Needed for ISAs with variable length instructions + // (i386, x86_64) or multiple instruction sizes (arm, thumb). + // Returns zero if the instruction length was successfully measured. + // as - The address space to use + // addr - The address of the instruction being measured + // length - Set to the length of the instruction + // arg - The driver-provided generic argument (see unw_init_remote) + int (*instruction_length)(unw_addr_space_t as, unw_word_t addr, + int *length, void *arg); + +} unw_accessors_t; + +extern int unw_init_remote(unw_cursor_t*, unw_addr_space_t, void*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern unw_accessors_t* unw_get_accessors(unw_addr_space_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern unw_addr_space_t unw_create_addr_space(unw_accessors_t*, unw_targettype_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern void unw_flush_caches(unw_addr_space_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_set_caching_policy(unw_addr_space_t, unw_caching_policy_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern void unw_destroy_addr_space(unw_addr_space_t asp) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern void unw_set_logging_level(unw_addr_space_t, FILE *, unw_log_level_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +// Should be called when remote unwinding if a bundle in the remote process +// is unloaded +extern void unw_image_was_unloaded(unw_addr_space_t, unw_word_t mh) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +// Try to discern where the function's prologue instructions end +// start - start address of the function, required +// end - first address beyond the function, or zero if unknown +// endofprologue - set to the address after the last prologue instruction if successful +extern int unw_end_of_prologue_setup(unw_cursor_t*, unw_word_t start, unw_word_t end, unw_word_t *endofprologue) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +/* + * Dynamic unwinding API + * NOT IMPLEMENTED on Mac OS X + * extern void _U_dyn_register(unw_dyn_info_t*); + * extern void _U_dyn_cancel(unw_dyn_info_t*); + */ + +#ifdef __cplusplus +} +#endif + +#pragma mark Register numbers + +// architecture independent register numbers +enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer +}; + + +// 32-bit x86 registers +enum { + UNW_X86_EAX = 0, + UNW_X86_ECX = 1, + UNW_X86_EDX = 2, + UNW_X86_EBX = 3, + UNW_X86_EBP = 4, + UNW_X86_ESP = 5, + UNW_X86_ESI = 6, + UNW_X86_EDI = 7 +}; + + +// 64-bit x86_64 registers +enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15 +}; + + +// 32-bit ppc register numbers +enum { + UNW_PPC_R0 = 0, + UNW_PPC_R1 = 1, + UNW_PPC_R2 = 2, + UNW_PPC_R3 = 3, + UNW_PPC_R4 = 4, + UNW_PPC_R5 = 5, + UNW_PPC_R6 = 6, + UNW_PPC_R7 = 7, + UNW_PPC_R8 = 8, + UNW_PPC_R9 = 9, + UNW_PPC_R10 = 10, + UNW_PPC_R11 = 11, + UNW_PPC_R12 = 12, + UNW_PPC_R13 = 13, + UNW_PPC_R14 = 14, + UNW_PPC_R15 = 15, + UNW_PPC_R16 = 16, + UNW_PPC_R17 = 17, + UNW_PPC_R18 = 18, + UNW_PPC_R19 = 19, + UNW_PPC_R20 = 20, + UNW_PPC_R21 = 21, + UNW_PPC_R22 = 22, + UNW_PPC_R23 = 23, + UNW_PPC_R24 = 24, + UNW_PPC_R25 = 25, + UNW_PPC_R26 = 26, + UNW_PPC_R27 = 27, + UNW_PPC_R28 = 28, + UNW_PPC_R29 = 29, + UNW_PPC_R30 = 30, + UNW_PPC_R31 = 31, + UNW_PPC_F0 = 32, + UNW_PPC_F1 = 33, + UNW_PPC_F2 = 34, + UNW_PPC_F3 = 35, + UNW_PPC_F4 = 36, + UNW_PPC_F5 = 37, + UNW_PPC_F6 = 38, + UNW_PPC_F7 = 39, + UNW_PPC_F8 = 40, + UNW_PPC_F9 = 41, + UNW_PPC_F10 = 42, + UNW_PPC_F11 = 43, + UNW_PPC_F12 = 44, + UNW_PPC_F13 = 45, + UNW_PPC_F14 = 46, + UNW_PPC_F15 = 47, + UNW_PPC_F16 = 48, + UNW_PPC_F17 = 49, + UNW_PPC_F18 = 50, + UNW_PPC_F19 = 51, + UNW_PPC_F20 = 52, + UNW_PPC_F21 = 53, + UNW_PPC_F22 = 54, + UNW_PPC_F23 = 55, + UNW_PPC_F24 = 56, + UNW_PPC_F25 = 57, + UNW_PPC_F26 = 58, + UNW_PPC_F27 = 59, + UNW_PPC_F28 = 60, + UNW_PPC_F29 = 61, + UNW_PPC_F30 = 62, + UNW_PPC_F31 = 63, + UNW_PPC_MQ = 64, + UNW_PPC_LR = 65, + UNW_PPC_CTR = 66, + UNW_PPC_AP = 67, + UNW_PPC_CR0 = 68, + UNW_PPC_CR1 = 69, + UNW_PPC_CR2 = 70, + UNW_PPC_CR3 = 71, + UNW_PPC_CR4 = 72, + UNW_PPC_CR5 = 73, + UNW_PPC_CR6 = 74, + UNW_PPC_CR7 = 75, + UNW_PPC_XER = 76, + UNW_PPC_V0 = 77, + UNW_PPC_V1 = 78, + UNW_PPC_V2 = 79, + UNW_PPC_V3 = 80, + UNW_PPC_V4 = 81, + UNW_PPC_V5 = 82, + UNW_PPC_V6 = 83, + UNW_PPC_V7 = 84, + UNW_PPC_V8 = 85, + UNW_PPC_V9 = 86, + UNW_PPC_V10 = 87, + UNW_PPC_V11 = 88, + UNW_PPC_V12 = 89, + UNW_PPC_V13 = 90, + UNW_PPC_V14 = 91, + UNW_PPC_V15 = 92, + UNW_PPC_V16 = 93, + UNW_PPC_V17 = 94, + UNW_PPC_V18 = 95, + UNW_PPC_V19 = 96, + UNW_PPC_V20 = 97, + UNW_PPC_V21 = 98, + UNW_PPC_V22 = 99, + UNW_PPC_V23 = 100, + UNW_PPC_V24 = 101, + UNW_PPC_V25 = 102, + UNW_PPC_V26 = 103, + UNW_PPC_V27 = 104, + UNW_PPC_V28 = 105, + UNW_PPC_V29 = 106, + UNW_PPC_V30 = 107, + UNW_PPC_V31 = 108, + UNW_PPC_VRSAVE = 109, + UNW_PPC_VSCR = 110, + UNW_PPC_SPE_ACC = 111, + UNW_PPC_SPEFSCR = 112 + +}; + + +}; // namespace lldb_private + + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h b/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h new file mode 100644 index 000000000000..bee2ad578d69 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h @@ -0,0 +1,212 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- compact_unwind_encoding.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef __COMPACT_UNWIND_ENCODING__ +#define __COMPACT_UNWIND_ENCODING__ + +#include + +namespace lldb_private { + +// +// Each final linked mach-o image has an optional __TEXT, __unwind_info section. +// This section is much smaller and faster to use than the __eh_frame section. +// + + + +// +// Compilers usually emit standard Dwarf FDEs. The linker recognizes standard FDEs and +// synthesizes a matching compact_unwind_encoding_t and adds it to the __unwind_info table. +// It is also possible for the compiler to emit __unwind_info entries for functions that +// have different unwind requirements at different ranges in the function. +// +typedef uint32_t compact_unwind_encoding_t; + + + +// +// The __unwind_info section is laid out for an efficient two level lookup. +// The header of the section contains a coarse index that maps function address +// to the page (4096 byte block) containing the unwind info for that function. +// + +#define UNWIND_SECTION_VERSION 1 +struct unwind_info_section_header +{ + uint32_t version; // UNWIND_SECTION_VERSION + uint32_t commonEncodingsArraySectionOffset; + uint32_t commonEncodingsArrayCount; + uint32_t personalityArraySectionOffset; + uint32_t personalityArrayCount; + uint32_t indexSectionOffset; + uint32_t indexCount; + // compact_unwind_encoding_t[] + // uintptr_t personalities[] + // unwind_info_section_header_index_entry[] + // unwind_info_section_header_lsda_index_entry[] +}; + +struct unwind_info_section_header_index_entry +{ + uint32_t functionOffset; + uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page + uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range +}; + +struct unwind_info_section_header_lsda_index_entry +{ + uint32_t functionOffset; + uint32_t lsdaOffset; +}; + +// +// There are two kinds of second level index pages: regular and compressed. +// A compressed page can hold up to 1021 entries, but it cannot be used +// if too many different encoding types are used. The regular page holds +// 511 entries. +// + +struct unwind_info_regular_second_level_entry +{ + uint32_t functionOffset; + compact_unwind_encoding_t encoding; +}; + +#define UNWIND_SECOND_LEVEL_REGULAR 2 +struct unwind_info_regular_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR + uint16_t entryPageOffset; + uint16_t entryCount; + // entry array +}; + +#define UNWIND_SECOND_LEVEL_COMPRESSED 3 +struct unwind_info_compressed_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED + uint16_t entryPageOffset; + uint16_t entryCount; + uint16_t encodingsPageOffset; + uint16_t encodingsCount; + // 32-bit entry array + // encodings array +}; + +#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF) +#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF) + + + +// architecture independent bits +enum { + UNWIND_IS_NOT_FUNCTION_START = 0x80000000, + UNWIND_HAS_LSDA = 0x40000000, + UNWIND_PERSONALITY_MASK = 0x30000000, +}; + + +// x86_64 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=dwarf +// rbp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_64_MODE_MASK = 0x0F000000, + UNWIND_X86_64_MODE_COMPATIBILITY = 0x00000000, + UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000, + UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_64_MODE_STACK_IND = 0x03000000, + UNWIND_X86_64_MODE_DWARF = 0x04000000, + + UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_64_REG_NONE = 0, + UNWIND_X86_64_REG_RBX = 1, + UNWIND_X86_64_REG_R12 = 2, + UNWIND_X86_64_REG_R13 = 3, + UNWIND_X86_64_REG_R14 = 4, + UNWIND_X86_64_REG_R15 = 5, + UNWIND_X86_64_REG_RBP = 6, +}; + + +// x86 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=dwarf +// ebp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_MODE_MASK = 0x0F000000, + UNWIND_X86_MODE_COMPATIBILITY = 0x00000000, + UNWIND_X86_MODE_EBP_FRAME = 0x01000000, + UNWIND_X86_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_MODE_STACK_IND = 0x03000000, + UNWIND_X86_MODE_DWARF = 0x04000000, + + UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_REG_NONE = 0, + UNWIND_X86_REG_EBX = 1, + UNWIND_X86_REG_ECX = 2, + UNWIND_X86_REG_EDX = 3, + UNWIND_X86_REG_EDI = 4, + UNWIND_X86_REG_ESI = 5, + UNWIND_X86_REG_EBP = 6, +}; + +}; // namespace lldb_private + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h b/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h new file mode 100644 index 000000000000..80b9d2881c22 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h @@ -0,0 +1,213 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- unwind.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// C interface to libuwind +// +// Source compatible with Level 1 Base ABI documented at: +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + + +#ifndef __UNWIND_H__ +#define __UNWIND_H__ + +#include +#include +#include + +namespace lldb_private { + +typedef enum { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +typedef enum { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16 // gcc extension to C++ ABI +} _Unwind_Action; + + +struct _Unwind_Context; // opaque +struct _Unwind_Exception; // forward declaration + +struct _Unwind_Exception { + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code reason, struct _Unwind_Exception* exc); + uintptr_t private_1; // non-zero means forced unwind + uintptr_t private_2; // holds sp that phase1 found for phase2 to use +}; + + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + struct _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context, + void* stop_parameter ); + + +typedef _Unwind_Reason_Code (*__personality_routine) + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + struct _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context); + + + +#ifdef __cplusplus +extern "C" { +#endif + +// +// The following are the base functions documented by the C++ ABI +// +#if __arm__ + extern _Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception* exception_object); + extern void _Unwind_SjLj_Resume(struct _Unwind_Exception* exception_object); +#else + extern _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object); + extern void _Unwind_Resume(struct _Unwind_Exception* exception_object); +#endif +extern void _Unwind_DeleteException(struct _Unwind_Exception* exception_object); +extern uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index); +extern void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value); +extern uintptr_t _Unwind_GetIP(struct _Unwind_Context* context); +extern void _Unwind_SetIP(struct _Unwind_Context*, uintptr_t new_value); +extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context); +extern uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context); +#if __arm__ + extern _Unwind_Reason_Code _Unwind_SjLj_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter ); +#else + extern _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter ); +#endif + +#if __arm__ + typedef struct _Unwind_FunctionContext* _Unwind_FunctionContext_t; + extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc); + extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc); +#endif + +// +// The following are semi-suppoted extensions to the C++ ABI +// + + +// +// called by __cxa_rethrow(). +// +#if __arm__ + extern _Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception* exception_object); +#else + extern _Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* exception_object); +#endif + + +// +// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the +// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack +// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON. +// +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context*, void*); +extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void*); + + +// +// _Unwind_GetCFA is a gcc extension that can be called from within a personality +// handler to get the CFA (stack pointer before call) of current frame. +// +extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context*); + + +// +// _Unwind_GetIPInfo is a gcc extension that can be called from within a personality +// handler. Similar to _Unwind_GetIP() but also returns in *ipBefore a non-zero +// value if the instruction pointer is at or before the instruction causing +// the unwind. Normally, in a function call, the IP returned is the return address +// which is after the call instruction and may be past the end of the function +// containing the call instruction. +// +extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore); + + +// +// __register_frame() is used with dynamically generated code to register the FDE +// for a generated (JIT) code. The FDE must use pc-rel addressing to point to its +// function and optional LSDA. __register_frame() has existed in all versions of +// Mac OS X, but in 10.4 and 10.5 it was buggy and did not actually register the +// FDE with the unwinder. In 10.6 and later it does register properly. +// +extern void __register_frame(const void* fde); +extern void __deregister_frame(const void* fde); + + +// +// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has +// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind info" +// which the runtime uses in preference to dwarf unwind info. This function +// will only work if the target function has an FDE but no compact unwind info. +// +struct dwarf_eh_bases +{ + uintptr_t tbase; + uintptr_t dbase; + uintptr_t func; +}; +extern const void* _Unwind_Find_FDE(const void* pc, struct dwarf_eh_bases*); + + +// +// This function attempts to find the start (address of first instruction) of +// a function given an address inside the function. It only works if the function +// has an FDE (dwarf unwind info). +// This function is unimplemented on Mac OS X 10.6 and later. Instead, use +// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result. +extern void* _Unwind_FindEnclosingFunction(void* pc); + + +// Mac OS X does not support text-rel and data-rel addressing so these functions are unimplemented +extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) __attribute__((unavailable)); +extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) __attribute__((unavailable)); + + + +// Mac OS X 10.4 and 10.5 had implementations of these functions in libgcc_s.dylib, +// but they never worked. These functions are no longer available. +extern void __register_frame_info_bases(const void* fde, void* ob, void* tb, void* db) __attribute__((unavailable)); +extern void __register_frame_info(const void* fde, void* ob) __attribute__((unavailable)); +extern void __register_frame_info_table_bases(const void* fde, void* ob,void* tb, void* db) __attribute__((unavailable)); +extern void __register_frame_info_table(const void* fde, void* ob) __attribute__((unavailable)); +extern void __register_frame_table(const void* fde) __attribute__((unavailable)); +extern void* __deregister_frame_info(const void* fde) __attribute__((unavailable)); +extern void* __deregister_frame_info_bases(const void* fde) __attribute__((unavailable)); + + +#ifdef __cplusplus +} +#endif + +}; // namespace lldb_private + +#endif // __UNWIND_H__ + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp new file mode 100644 index 000000000000..5173dc0068e0 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp @@ -0,0 +1,456 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- AddressSpace.hpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include +#include +#include +#include +#include +#include +#if !defined (SUPPORT_REMOTE_UNWINDING) +#include +#endif +#include +#include +#include + +#include "FileAbstraction.hpp" +#include "libunwind.h" +#include "InternalMacros.h" +#include "dwarf2.h" +#include "RemoteProcInfo.hpp" + +#if defined (SUPPORT_REMOTE_UNWINDING) +bool _dyld_find_unwind_sections(void* addr, void* info) +{ + assert("unwinding with a non-remote process not supported."); + return false; +} +#endif // SUPPORT_REMOTE_UNWINDING + +namespace lldb_private { + +/// +/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the same process. It compiles away and making local unwinds very fast. +/// +class LocalAddressSpace +{ +public: + + #if __LP64__ + typedef uint64_t pint_t; + typedef int64_t sint_t; + #else + typedef uint32_t pint_t; + typedef int32_t sint_t; + #endif + int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { memcpy(buf, (void*)addr, extent); return 1; } + uint8_t get8(pint_t addr) { return *((uint8_t*)addr); } + uint16_t get16(pint_t addr) { return *((uint16_t*)addr); } + uint32_t get32(pint_t addr) { return *((uint32_t*)addr); } + uint64_t get64(pint_t addr) { return *((uint64_t*)addr); } + double getDouble(pint_t addr) { return *((double*)addr); } + v128 getVector(pint_t addr) { return *((v128*)addr); } + + uint8_t get8(pint_t addr, int& err) { return *((uint8_t*)addr); err = 0; } + uint16_t get16(pint_t addr, int& err) { return *((uint16_t*)addr); err = 0; } + uint32_t get32(pint_t addr, int& err) { return *((uint32_t*)addr); err = 0; } + uint64_t get64(pint_t addr, int& err) { return *((uint64_t*)addr); err = 0; } + double getDouble(pint_t addr, int& err) { return *((double*)addr); err = 0; } + v128 getVector(pint_t addr, int& err) { return *((v128*)addr); err = 0; } + + uintptr_t getP(pint_t addr); + uintptr_t getP(pint_t addr, int &err); + static uint64_t getULEB128(pint_t& addr, pint_t end); + static int64_t getSLEB128(pint_t& addr, pint_t end); + + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart); + +#if defined (SUPPORT_REMOTE_UNWINDING) + RemoteProcInfo* getRemoteProcInfo () { return NULL; } + unw_accessors_t* accessors() { return NULL; } + unw_addr_space_t wrap() { return NULL; } +#endif +}; + +LocalAddressSpace sThisAddress; + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) +{ +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif +} + +inline uintptr_t LocalAddressSpace::getP(pint_t addr, int &err) +{ +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif + err = 0; +} + +/* Read a ULEB128 into a 64-bit word. */ +inline uint64_t +LocalAddressSpace::getULEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + const uint8_t* pend = (uint8_t*)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if ( p == pend ) + ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + ABORT("malformed uleb128 expression"); + } + else { + result |= b << bit; + bit += 7; + } + } while ( *p++ >= 0x80 ); + addr = (pint_t)p; + return result; +} + +/* Read a SLEB128 into a 64-bit word. */ +inline int64_t +LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + addr = (pint_t)p; + return result; +} + +LocalAddressSpace::pint_t +LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + const uint8_t* p = (uint8_t*)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + ABORT("unknown pointer encoding"); + break; + } + + if ( encoding & DW_EH_PE_indirect ) + result = getP(result); + + return result; +} + + +inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart) +{ +#if !defined (SUPPORT_REMOTE_UNWINDING) + dyld_unwind_sections info; + if ( _dyld_find_unwind_sections((void*)addr, &info) ) { + mh = (pint_t)info.mh; + dwarfStart = (pint_t)info.dwarf_section; + dwarfLen = (pint_t)info.dwarf_section_length; + compactStart = (pint_t)info.compact_unwind_section; + return true; + } +#else + assert("unwinding with a non-remote process not supported."); +#endif + return false; +} + + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + dl_info dyldInfo; + if ( dladdr((void*)addr, &dyldInfo) ) { + if ( dyldInfo.dli_sname != NULL ) { + strlcpy(buf, dyldInfo.dli_sname, bufLen); + *offset = (addr - (pint_t)dyldInfo.dli_saddr); + return true; + } + } + return false; +} + +#if defined (SUPPORT_REMOTE_UNWINDING) +/// +/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the another process. The other process can be a different endianness and a different +/// pointer size and is handled by the P template parameter. +/// +template +class OtherAddressSpace +{ +public: + OtherAddressSpace (unw_addr_space_t remote_addr_space, void* arg) : fAddrSpace ((unw_addr_space_remote *)remote_addr_space), fArg(arg) + { + if (fAddrSpace->type != UNW_REMOTE) + ABORT("OtherAddressSpace ctor called with non-remote address space."); + fRemoteProcInfo = fAddrSpace->ras; + } + + typedef typename P::uint_t pint_t; + typedef typename P::int_t sint_t; + + int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { return fRemoteProcInfo->getBytes (addr, extent, buf, fArg); } + uint8_t get8(pint_t addr) { return fRemoteProcInfo->get8(addr, fArg); } + uint16_t get16(pint_t addr) { return fRemoteProcInfo->get16(addr, fArg); } + uint32_t get32(pint_t addr) { return fRemoteProcInfo->get32(addr, fArg); } + uint64_t get64(pint_t addr) { return fRemoteProcInfo->get64(addr, fArg); } + pint_t getP(pint_t addr) { return fRemoteProcInfo->getP(addr, fArg); } + + uint8_t get8(pint_t addr, int& err) { return fRemoteProcInfo->get8(addr, err, fArg); } + uint16_t get16(pint_t addr, int& err) { return fRemoteProcInfo->get16(addr, err, fArg); } + uint32_t get32(pint_t addr, int& err) { return fRemoteProcInfo->get32(addr, err, fArg); } + uint64_t get64(pint_t addr, int& err) { return fRemoteProcInfo->get64(addr, err, fArg); } + pint_t getP(pint_t addr, int &err) { return fRemoteProcInfo->getP(addr, err, fArg); } + + uint64_t getULEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getULEB128 (addr, end, fArg); } + int64_t getSLEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getSLEB128 (addr, end, fArg); } + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + double getDouble(pint_t addr); + v128 getVector(pint_t addr); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findFunctionExtent(pint_t addr, unw_word_t* begin, unw_word_t* end); + bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart); + RemoteProcInfo* getRemoteProcInfo () { return fRemoteProcInfo; } + unw_accessors_t* accessors() { return fRemoteProcInfo->getAccessors(); } + unw_addr_space_t wrap() { return (unw_addr_space_t) fAddrSpace; } +private: + void* localCopy(pint_t addr); + unw_addr_space_remote *fAddrSpace; + RemoteProcInfo* fRemoteProcInfo; + void* fArg; +}; + +template +typename OtherAddressSpace

::pint_t OtherAddressSpace

::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + pint_t p = addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = fRemoteProcInfo->getP(addr, fArg); + p += sizeof(pint_t); + addr = p; + break; + case DW_EH_PE_uleb128: + result = fRemoteProcInfo->getULEB128(addr, end, fArg); + break; + case DW_EH_PE_udata2: + result = fRemoteProcInfo->get16(addr, fArg); + p += 2; + addr = p; + break; + case DW_EH_PE_udata4: + result = fRemoteProcInfo->get32(addr, fArg); + p += 4; + addr = p; + break; + case DW_EH_PE_udata8: + result = fRemoteProcInfo->get64(addr, fArg); + p += 8; + addr = p; + break; + case DW_EH_PE_sleb128: + result = fRemoteProcInfo->getSLEB128(addr, end, fArg); + break; + case DW_EH_PE_sdata2: + result = (int16_t)fRemoteProcInfo->get16(addr, fArg); + p += 2; + addr = p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)fRemoteProcInfo->get32(addr, fArg); + p += 4; + addr = p; + break; + case DW_EH_PE_sdata8: + result = fRemoteProcInfo->get64(addr, fArg); + p += 8; + addr = p; + break; + default: + ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + ABORT("unknown pointer encoding"); + break; + } + + if ( encoding & DW_EH_PE_indirect ) + result = fRemoteProcInfo->getP(result, fArg); + + return result; +} + +template +double OtherAddressSpace

::getDouble(pint_t addr) +{ + return fRemoteProcInfo->getDouble(addr, fArg); +} + +template +v128 OtherAddressSpace

::getVector(pint_t addr) +{ + return fRemoteProcInfo->getVector(addr, fArg); +} + +template +bool OtherAddressSpace

::findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart) +{ + compactStart = 0; + uint64_t t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start; + if (fRemoteProcInfo->getImageAddresses (addr, t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start, fArg)) + { + mh = t_mh; + eh_frame_start = t_eh_frame_start; + eh_frame_len = t_eh_frame_len; + compactStart = t_compact_start; + return true; + } + return false; +} + +template +bool OtherAddressSpace

::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + return fRemoteProcInfo->findFunctionName (addr, buf, bufLen, offset, fArg); +} + +#endif // SUPPORT_REMOTE_UNWINDING + + +} // namespace lldb_private + + + +#endif // __ADDRESSSPACE_HPP__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp new file mode 100644 index 000000000000..d19d7aea50fa --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp @@ -0,0 +1,115 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- ArchDefaultUnwinder.hpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Unwind a stack frame using nothing but the default conventions on +// this architecture. + +#ifndef __ARCH_DEFAULT_UNWINDER_HPP +#define __ARCH_DEFAULT_UNWINDER_HPP + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "RemoteRegisterMap.hpp" +#include "RemoteProcInfo.hpp" + +namespace lldb_private +{ + +// As a last ditch attempt to unwind a stack frame, unwind by the +// architecture's typical conventions. We try compact unwind, eh frame CFI, +// and then assembly profiling if we have function bounds -- but if we're +// looking at an address with no function bounds or unwind info, make a best +// guess at how to get out. + +// In practice, this is usually hit when we try to step out of start() in a +// stripped application binary, we've jumped to 0x0, or we're in jitted code +// in the heap. + +template +int stepByArchitectureDefault_x86 (A& addressSpace, R& registers, + uint64_t pc, int wordsize) { + R newRegisters(registers); + RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap(); + int frame_reg = rmap->unwind_regno_for_frame_pointer(); + int stack_reg = rmap->unwind_regno_for_stack_pointer(); + int err; + + /* If the pc is 0x0 either we call'ed 0 (went thorugh a null function + pointer) or this is a thread in the middle of being created that has + no stack at all. + For the call-0x0 case, we know how to unwind that - the pc is at + the stack pointer. + + Otherwise follow the usual convention of trusting that RBP/EBP has the + start of the stack frame and we can find the caller's pc based on + that. */ + + uint64_t newpc, newframeptr; + newpc = 0; + newframeptr = -1; + if (pc == 0) { + uint64_t oldsp = registers.getRegister(stack_reg); + err = 0; + if (oldsp != 0) { + newpc = addressSpace.getP(registers.getRegister(stack_reg), err); + if (err != 0) + return UNW_EUNSPEC; + newRegisters.setIP (newpc); + newRegisters.setRegister (stack_reg, registers.getRegister(stack_reg) + + wordsize); + } + } + else { + newpc = addressSpace.getP(registers.getRegister(frame_reg) + + wordsize, err); + if (err != 0) + return UNW_EUNSPEC; + + newRegisters.setIP (newpc); + newframeptr = addressSpace.getP(registers.getRegister(frame_reg), + err); + if (err != 0) + return UNW_EUNSPEC; + + newRegisters.setRegister (frame_reg, newframeptr); + newRegisters.setRegister (stack_reg, registers.getRegister(frame_reg) + + (wordsize * 2)); + } + registers = newRegisters; + if (newpc == 0 || newframeptr == 0) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; +} + +template +int stepByArchitectureDefault (A& addressSpace, Registers_x86_64 ®isters, + uint64_t pc) { + return stepByArchitectureDefault_x86 (addressSpace, registers, pc, 8); +} + +template +int stepByArchitectureDefault (A& addressSpace, Registers_x86& registers, + uint64_t pc) { + return stepByArchitectureDefault_x86 (addressSpace, registers, pc, 4); +} + +template +int stepByArchitectureDefault (A& addressSpace, Registers_ppc& registers, + uint64_t pc) { + ABORT("Remote unwinding not supported for ppc."); + return UNW_EUNSPEC; +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING +#endif // __ARCH_DEFAULT_UNWINDER_HPP diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp new file mode 100644 index 000000000000..1e695d5e4f0f --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp @@ -0,0 +1,147 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- AssemblyInstructions.hpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __ASSEMBLY_INSTRUCTIONS_HPP +#define __ASSEMBLY_INSTRUCTIONS_HPP + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include +#include +#include +#include +#include +#include + +#include "libunwind.h" +#include "AssemblyParser.hpp" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "RemoteUnwindProfile.h" + +namespace lldb_private +{ + +// A debug function to dump the contents of an RemoteUnwindProfile to +// stdout in a human readable form. + +template +void printProfile (A& addressSpace, uint64_t pc, RemoteUnwindProfile* profile, R& registers) { + RemoteProcInfo *procinfo = addressSpace.getRemoteProcInfo(); + RemoteRegisterMap *regmap = procinfo->getRegisterMap(); + + procinfo->logDebug ("Print profile: given pc of 0x%llx, profile has range 0x%llx - 0x%llx", pc, profile->fStart, profile->fEnd); + procinfo->logDebug ("CFA locations:"); + std::map::iterator i; + for (i = profile->cfa.begin(); i != profile->cfa.end(); ++i) { + procinfo->logDebug (" as of 0x%llx cfa is based off of reg %d (%s) offset %d", i->first, i->second.regno, regmap->unwind_regno_to_name(i->second.regno), i->second.offset); + } + procinfo->logDebug ("Caller's saved IP is at %d bytes offset from the cfa", (int)profile->returnAddress.value); + procinfo->logDebug ("Register saves:"); + std::map >::iterator j; + for (j = profile->saved_registers.begin(); j != profile->saved_registers.end(); ++j) { + char *tbuf1, *tbuf2, *tbuf3; + asprintf (&tbuf1, " at pc 0x%llx there are %d registers saved ", j->first, (int) j->second.size()); + std::vector::iterator k; + for (k = j->second.begin(); k != j->second.end(); ++k) { + if (k->location == RemoteUnwindProfile::kRegisterOffsetFromCFA) { + asprintf (&tbuf2, "[reg %d (%s) is %d bytes from cfa] ", k->regno, regmap->unwind_regno_to_name(k->regno), (int) k->value); + int newlen = strlen (tbuf1) + strlen (tbuf2) + 1; + tbuf3 = (char *) malloc (newlen); + strcpy (tbuf3, tbuf1); + strcat (tbuf3, tbuf2); + free (tbuf1); + free (tbuf2); + tbuf1 = tbuf3; + } + if (k->location == RemoteUnwindProfile::kRegisterIsCFA) { + asprintf (&tbuf2, "[reg %d (%s) is the same as the cfa] ", k->regno, regmap->unwind_regno_to_name(k->regno)); + int newlen = strlen (tbuf1) + strlen (tbuf2) + 1; + tbuf3 = (char *) malloc (newlen); + strcpy (tbuf3, tbuf1); + strcat (tbuf3, tbuf2); + free (tbuf1); + free (tbuf2); + tbuf1 = tbuf3; + } + } + procinfo->logDebug ("%s", tbuf1); + free (tbuf1); + } +} + +template +int stepWithAssembly (A& addressSpace, uint64_t pc, RemoteUnwindProfile* profile, R& registers) { + R newRegisters(registers); + RemoteProcInfo *procinfo = addressSpace.getRemoteProcInfo(); + if (pc > profile->fEnd) + ABORT("stepWithAssembly called with pc not in RemoteUnwindProfile's bounds"); + + if (procinfo && (procinfo->getDebugLoggingLevel() & UNW_LOG_LEVEL_DEBUG)) + printProfile (addressSpace, pc, profile, registers); + + std::map::iterator i = profile->cfa.lower_bound (pc); + if (i == profile->cfa.begin() && i == profile->cfa.end()) + return UNW_EINVAL; + if (i == profile->cfa.end()) { + --i; + } else { + if (i != profile->cfa.begin() && i->first != pc) + --i; + } + + uint64_t cfa = registers.getRegister (i->second.regno) + i->second.offset; + + std::map >::iterator j; + + for (j = profile->saved_registers.begin(); j != profile->saved_registers.end() && j->first <= pc; ++j) { + std::vector::iterator k = j->second.begin(); + for (; k != j->second.end(); ++k) { + RemoteUnwindProfile::SavedReg sr = *k; + if (sr.type == RemoteUnwindProfile::kGeneralPurposeRegister) { + uint64_t result; + int err = 0; + switch (sr.location) { + case RemoteUnwindProfile::kRegisterOffsetFromCFA: + result = addressSpace.getP(cfa + sr.value, err); + break; + case RemoteUnwindProfile::kRegisterIsCFA: + result = cfa; + break; + default: + ABORT("Unknown saved register location in stepWithAssembly."); + } + // If we failed to read remote memory, stop unwinding. + if (err) + return UNW_STEP_END; + newRegisters.setRegister (sr.regno, result); + } + } + } + newRegisters.setSP(cfa); + uint64_t ip = addressSpace.getP(cfa + profile->returnAddress.value); + if (ip == 0) + return UNW_STEP_END; + newRegisters.setIP(ip); + registers = newRegisters; + return UNW_STEP_SUCCESS; +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING +#endif //ASSEMBLY_INSTRUCTIONS_HPP diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp new file mode 100644 index 000000000000..b34f93f50c65 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp @@ -0,0 +1,409 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- AssemblyParser.hpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Disassemble the prologue instructions in functions, create a profile +// of stack movements and register saves performed therein. + +#ifndef __ASSEMBLY_PARSER_HPP +#define __ASSEMBLY_PARSER_HPP + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "libunwind.h" +#include "RemoteProcInfo.hpp" +#include "Registers.hpp" +#include "FileAbstraction.hpp" +#include "AddressSpace.hpp" +#include "RemoteUnwindProfile.h" + +namespace lldb_private +{ + +// Analyze the instructions in an x86_64/i386 function prologue, fill out an RemoteUnwindProfile. + +class AssemblyParse_x86 { +public: + AssemblyParse_x86 (RemoteProcInfo& procinfo, unw_accessors_t *acc, unw_addr_space_t as, void *arg) : fArg(arg), fAccessors(acc), fAs(as), fRemoteProcInfo(procinfo) { + fRegisterMap = fRemoteProcInfo.getRegisterMap(); + if (fRemoteProcInfo.getTargetArch() == UNW_TARGET_X86_64) { + fStackPointerRegnum = UNW_X86_64_RSP; + fFramePointerRegnum = UNW_X86_64_RBP; + fWordSize = 8; + } else { + fStackPointerRegnum = UNW_X86_ESP; + fFramePointerRegnum = UNW_X86_EBP; + fWordSize = 4; + } + } + + uint32_t extract_4_LE (uint8_t *b) { + uint32_t v = 0; + for (int i = 3; i >= 0; i--) + v = (v << 8) | b[i]; + return v; + } + + bool push_rbp_pattern_p (); + bool push_0_pattern_p (); + bool mov_rsp_rbp_pattern_p (); + bool sub_rsp_pattern_p (int *amount); + bool push_reg_p (int *regno); + bool mov_reg_to_local_stack_frame_p (int *regno, int *rbp_offset); + bool ret_pattern_p (); + bool profileFunction (uint64_t start, uint64_t end, RemoteUnwindProfile& profile); + +private: + + void *fArg; + uint8_t* fCurInsnByteBuf; + int fCurInsnSize; + RemoteProcInfo& fRemoteProcInfo; + RemoteRegisterMap *fRegisterMap; + unw_accessors_t *fAccessors; + unw_addr_space_t fAs; + int fWordSize; + int fStackPointerRegnum; + int fFramePointerRegnum; +}; + +// Macro to detect if this is a REX mode prefix byte. +#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48) + +// The high bit which should be added to the source register number (the "R" bit) +#define REX_W_SRCREG(opcode) (((opcode) & 0x4) >> 2) + +// The high bit which should be added to the destination register number (the "B" bit) +#define REX_W_DSTREG(opcode) ((opcode) & 0x1) + +// pushq %rbp [0x55] +bool AssemblyParse_x86::push_rbp_pattern_p () { + uint8_t *p = fCurInsnByteBuf; + if (*p == 0x55) + return true; + return false; +} + +// pushq $0 ; the first instruction in start() [0x6a 0x00] +bool AssemblyParse_x86::push_0_pattern_p () +{ + uint8_t *p = fCurInsnByteBuf; + if (*p == 0x6a && *(p + 1) == 0x0) + return true; + return false; +} + +// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] +// movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5] +bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () { + uint8_t *p = fCurInsnByteBuf; + if (fWordSize == 8 && *p == 0x48) + p++; + if (*(p) == 0x8b && *(p + 1) == 0xec) + return true; + if (*(p) == 0x89 && *(p + 1) == 0xe5) + return true; + return false; +} + +// subq $0x20, %rsp +bool AssemblyParse_x86::sub_rsp_pattern_p (int *amount) { + uint8_t *p = fCurInsnByteBuf; + if (fWordSize == 8 && *p == 0x48) + p++; + // 8-bit immediate operand + if (*p == 0x83 && *(p + 1) == 0xec) { + *amount = (int8_t) *(p + 2); + return true; + } + // 32-bit immediate operand + if (*p == 0x81 && *(p + 1) == 0xec) { + *amount = (int32_t) extract_4_LE (p + 2); + return true; + } + // Not handled: [0x83 0xc4] for imm8 with neg values + // [0x81 0xc4] for imm32 with neg values + return false; +} + +// pushq %rbx +// pushl $ebx +bool AssemblyParse_x86::push_reg_p (int *regno) { + uint8_t *p = fCurInsnByteBuf; + int regno_prefix_bit = 0; + // If we have a rex prefix byte, check to see if a B bit is set + if (fWordSize == 8 && *p == 0x41) { + regno_prefix_bit = 1 << 3; + p++; + } + if (*p >= 0x50 && *p <= 0x57) { + int r = (*p - 0x50) | regno_prefix_bit; + if (fRegisterMap->machine_regno_to_unwind_regno (r, *regno) == true) { + return true; + } + } + return false; +} + +// Look for an instruction sequence storing a nonvolatile register +// on to the stack frame. + +// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0] +// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4] +bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int *regno, int *rbp_offset) { + uint8_t *p = fCurInsnByteBuf; + int src_reg_prefix_bit = 0; + int target_reg_prefix_bit = 0; + + if (fWordSize == 8 && REX_W_PREFIX_P (*p)) { + src_reg_prefix_bit = REX_W_SRCREG (*p) << 3; + target_reg_prefix_bit = REX_W_DSTREG (*p) << 3; + if (target_reg_prefix_bit == 1) { + // rbp/ebp don't need a prefix bit - we know this isn't the + // reg we care about. + return false; + } + p++; + } + + if (*p == 0x89) { + /* Mask off the 3-5 bits which indicate the destination register + if this is a ModR/M byte. */ + int opcode_destreg_masked_out = *(p + 1) & (~0x38); + + /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101 + and three bits between them, e.g. 01nnn101 + We're looking for a destination of ebp-disp8 or ebp-disp32. */ + int immsize; + if (opcode_destreg_masked_out == 0x45) + immsize = 2; + else if (opcode_destreg_masked_out == 0x85) + immsize = 4; + else + return false; + + int offset = 0; + if (immsize == 2) + offset = (int8_t) *(p + 2); + if (immsize == 4) + offset = (uint32_t) extract_4_LE (p + 2); + if (offset > 0) + return false; + + int savedreg = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit; + if (fRegisterMap->machine_regno_to_unwind_regno (savedreg, *regno) == true) { + *rbp_offset = offset > 0 ? offset : -offset; + return true; + } + } + return false; +} + +// ret [0xc9] or [0xc2 imm8] or [0xca imm8] +bool AssemblyParse_x86::ret_pattern_p () { + uint8_t *p = fCurInsnByteBuf; + if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3) + return true; + return false; +} + +bool AssemblyParse_x86::profileFunction (uint64_t start, uint64_t end, RemoteUnwindProfile& profile) { + if (start == -1 || end == 0) + return false; + + profile.fStart = start; + profile.fEnd = end; + profile.fRegSizes[RemoteUnwindProfile::kGeneralPurposeRegister] = fWordSize; + profile.fRegSizes[RemoteUnwindProfile::kFloatingPointRegister] = 8; + profile.fRegSizes[RemoteUnwindProfile::kVectorRegister] = 16; + + // On function entry, the CFA is rsp+fWordSize + + RemoteUnwindProfile::CFALocation initial_cfaloc; + initial_cfaloc.regno = fStackPointerRegnum; + initial_cfaloc.offset = fWordSize; + profile.cfa[start] = initial_cfaloc; + + // The return address is at CFA - fWordSize + // CFA doesn't change value during the lifetime of the function (hence "C") + // so the returnAddress is the same for the duration of the function. + + profile.returnAddress.regno = 0; + profile.returnAddress.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + profile.returnAddress.value = -fWordSize; + profile.returnAddress.adj = 0; + profile.returnAddress.type = RemoteUnwindProfile::kGeneralPurposeRegister; + + // The caller's rsp has the same value as the CFA at all points during + // this function's lifetime. + + RemoteUnwindProfile::SavedReg rsp_loc; + rsp_loc.regno = fStackPointerRegnum; + rsp_loc.location = RemoteUnwindProfile::kRegisterIsCFA; + rsp_loc.value = 0; + rsp_loc.adj = 0; + rsp_loc.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[start].push_back(rsp_loc); + profile.fRegistersSaved[fStackPointerRegnum] = 1; + + int non_prologue_insn_count = 0; + int insn_count = 0; + uint64_t cur_addr = start; + uint64_t first_insn_past_prologue = start; + int push_rbp_seen = 0; + int current_cfa_register = fStackPointerRegnum; + int sp_adjustments = 0; + + while (cur_addr < end && non_prologue_insn_count < 10) + { + int offset, regno; + uint64_t next_addr; + insn_count++; + int is_prologue_insn = 0; + + if (fAccessors->instruction_length (fAs, cur_addr, &fCurInsnSize, fArg) != 0) { + /* An error parsing the instruction; stop scanning. */ + break; + } + fCurInsnByteBuf = (uint8_t *) malloc (fCurInsnSize); + if (fRemoteProcInfo.getBytes (cur_addr, fCurInsnSize, fCurInsnByteBuf, fArg) == 0) + return false; + next_addr = cur_addr + fCurInsnSize; + + // start () opens with a 'push $0x0' which is in the saved ip slot on the stack - + // so we know to stop backtracing here. We need to ignore this instruction. + if (push_0_pattern_p () && push_rbp_seen == 0 && insn_count == 1) + { + cur_addr = next_addr; + first_insn_past_prologue = next_addr; + continue; + } + + if (push_rbp_pattern_p () && push_rbp_seen == 0) + { + if (current_cfa_register == fStackPointerRegnum) { + sp_adjustments -= fWordSize; + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fStackPointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + } + + RemoteUnwindProfile::SavedReg sreg; + sreg.regno = fFramePointerRegnum; + sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + sreg.value = sp_adjustments - fWordSize; + sreg.adj = 0; + sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[next_addr].push_back(sreg); + + push_rbp_seen = 1; + profile.fRegistersSaved[fFramePointerRegnum] = 1; + is_prologue_insn = 1; + goto next_iteration; + } + if (mov_rsp_rbp_pattern_p ()) { + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fFramePointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + current_cfa_register = fFramePointerRegnum; + is_prologue_insn = 1; + goto next_iteration; + } + if (ret_pattern_p ()) { + break; + } + if (sub_rsp_pattern_p (&offset)) { + sp_adjustments -= offset; + if (current_cfa_register == fStackPointerRegnum) { + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fStackPointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + } + is_prologue_insn = 1; + } + if (push_reg_p (®no)) { + sp_adjustments -= fWordSize; + if (current_cfa_register == fStackPointerRegnum) { + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fStackPointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + is_prologue_insn = 1; + } + if (fRegisterMap->nonvolatile_reg_p (regno) && profile.fRegistersSaved[regno] == 0) { + RemoteUnwindProfile::SavedReg sreg; + sreg.regno = regno; + sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + sreg.value = sp_adjustments - fWordSize; + sreg.adj = 0; + sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[next_addr].push_back(sreg); + profile.fRegistersSaved[regno] = 1; + is_prologue_insn = 1; + } + } + if (mov_reg_to_local_stack_frame_p (®no, &offset) + && fRegisterMap->nonvolatile_reg_p (regno) + && profile.fRegistersSaved[regno] == 0) { + RemoteUnwindProfile::SavedReg sreg; + sreg.regno = regno; + sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + sreg.value = offset - fWordSize; + sreg.adj = 0; + sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[next_addr].push_back(sreg); + profile.fRegistersSaved[regno] = 1; + is_prologue_insn = 1; + } +next_iteration: + if (is_prologue_insn) { + first_insn_past_prologue = next_addr; + non_prologue_insn_count = 0; + } + cur_addr = next_addr; + non_prologue_insn_count++; + } + profile.fFirstInsnPastPrologue = first_insn_past_prologue; + return true; +} + + + + +bool AssemblyParse (RemoteProcInfo *procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, RemoteUnwindProfile &profile, void *arg) { + if (procinfo->getTargetArch() == UNW_TARGET_X86_64 || procinfo->getTargetArch() == UNW_TARGET_I386) { + AssemblyParse_x86 parser(*procinfo, acc, as, arg); + return parser.profileFunction (start, end, profile); + } else { + ABORT("Only x86_64 and i386 assembly parsing supported at this time"); + return false; + } +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING +#endif //ASSEMBLY_PARSER_HPP diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp new file mode 100644 index 000000000000..dda2308ada6f --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp @@ -0,0 +1,1019 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- CompactUnwinder.hpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __COMPACT_UNWINDER_HPP__ +#define __COMPACT_UNWINDER_HPP__ + +#include +#include +#include + +#include +#include + +#include "AddressSpace.hpp" +#include "Registers.hpp" + + + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + +#define SUPPORT_OLD_BINARIES 0 + +namespace lldb_private { + + + +/// +/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka unwind) by +/// modifying a Registers_x86 register set +/// +template +class CompactUnwinder_x86 +{ +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t info, uint32_t functionStart, A& addressSpace, Registers_x86& registers); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A& addressSpace, Registers_x86& registers); + static void framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers); + static int stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers); + static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers, bool indirectStackSize); +#if SUPPORT_OLD_BINARIES + static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers); +#endif +}; + + + +template +int CompactUnwinder_x86::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) +{ + //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); + switch ( compactEncoding & UNWIND_X86_MODE_MASK ) { +#if SUPPORT_OLD_BINARIES + case UNWIND_X86_MODE_COMPATIBILITY: + return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers); +#endif + case UNWIND_X86_MODE_EBP_FRAME: + return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, addressSpace, registers); + case UNWIND_X86_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, false); + case UNWIND_X86_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, true); + } + ABORT("invalid compact unwind encoding"); +} + + +template +int CompactUnwinder_x86::stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A& addressSpace, Registers_x86& registers) +{ + uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getEBP() - 4*savedRegistersOffset; + for (int i=0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + default: + DEBUG_MESSAGE("bad register for EBP frame, encoding=%08X for function starting at 0x%X\n", compactEncoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + + +template +int CompactUnwinder_x86::stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding, uint32_t functionStart, + A& addressSpace, Registers_x86& registers, bool indirectStackSize) +{ + uint32_t stackSizeEncoded = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded*4; + if ( indirectStackSize ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart+stackSizeEncoded); + stackSize = subl + 4*stackAdjust; + } + // decompress permutation + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 4 - 4*regCount; + for (uint32_t i=0; i < regCount; ++i) { + switch ( registersSaved[i] ) { + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EBP: + registers.setEBP(addressSpace.get32(savedRegisters)); + break; + default: + DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%X\n", encoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +#if SUPPORT_OLD_BINARIES +template +int CompactUnwinder_x86::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) +{ + //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); + typename A::pint_t savedRegisters; + uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_SIZE); + uint32_t stackSize; + uint32_t stackAdjust; + switch (compactEncoding & UNWIND_X86_CASE_MASK ) { + case UNWIND_X86_UNWIND_INFO_UNSPECIFIED: + return UNW_ENOINFO; + + case UNWIND_X86_EBP_FRAME_NO_REGS: + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX: + savedRegisters = registers.getEBP() - 4; + registers.setEBX(addressSpace.get32(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_ESI: + savedRegisters = registers.getEBP() - 4; + registers.setESI(addressSpace.get32(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EDI: + savedRegisters = registers.getEBP() - 4; + registers.setEDI(addressSpace.get32(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX_ESI: + savedRegisters = registers.getEBP() - 8; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_ESI_EDI: + savedRegisters = registers.getEBP() - 8; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX_ESI_EDI: + savedRegisters = registers.getEBP() - 12; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX_EDI: + savedRegisters = registers.getEBP() - 8; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_NO_REGS: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*0; + framelessUnwind(addressSpace, savedRegisters+4*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEBX(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_ESI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setESI(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EDI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEDI(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX_ESI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_ESI_EDI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_ESI_EDI_EBP: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + registers.setEBP(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX_ESI_EDI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX_ESI_EDI_EBP: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*4; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + registers.setEBP(addressSpace.get32(savedRegisters+12)); + framelessUnwind(addressSpace, savedRegisters+4*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_NO_REGS: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*0; + framelessUnwind(addressSpace, savedRegisters+4*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EBX: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEBX(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_ESI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setESI(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EDI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEDI(addressSpace.get32(savedRegisters)); + return UNW_STEP_SUCCESS; + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + + case UNWIND_X86_IND_STK_EBX_ESI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_ESI_EDI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_ESI_EDI_EBP: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + registers.setEBP(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EBX_ESI_EDI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EBX_ESI_EDI_EBP: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*4; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + registers.setEBP(addressSpace.get32(savedRegisters+12)); + framelessUnwind(addressSpace, savedRegisters+4*4, registers); + return UNW_STEP_SUCCESS; + + default: + DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%X\n", + compactEncoding & UNWIND_X86_CASE_MASK, functionStart); + ABORT("unknown compact unwind encoding"); + } + return UNW_EINVAL; +} +#endif // SUPPORT_OLD_BINARIES + + + +template +void CompactUnwinder_x86::frameUnwind(A& addressSpace, Registers_x86& registers) +{ + typename A::pint_t bp = registers.getEBP(); + // ebp points to old ebp + registers.setEBP(addressSpace.get32(bp)); + // old esp is ebp less saved ebp and return address + registers.setSP(bp+8); + // pop return address into eip + registers.setIP(addressSpace.get32(bp+4)); +} + +template +void CompactUnwinder_x86::framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers) +{ + // return address is on stack after last saved register + registers.setIP(addressSpace.get32(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation+4); +} + + + + + +/// +/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka unwind) by +/// modifying a Registers_x86_64 register set +/// +template +class CompactUnwinder_x86_64 +{ +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A& addressSpace, Registers_x86_64& registers); + static void framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers); + static int stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); + static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers, bool indirectStackSize); +#if SUPPORT_OLD_BINARIES + static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); +#endif +}; + + +template +int CompactUnwinder_x86_64::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) +{ + //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); + switch ( compactEncoding & UNWIND_X86_64_MODE_MASK ) { +#if SUPPORT_OLD_BINARIES + case UNWIND_X86_64_MODE_COMPATIBILITY: + return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers); +#endif + case UNWIND_X86_64_MODE_RBP_FRAME: + return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, addressSpace, registers); + case UNWIND_X86_64_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, false); + case UNWIND_X86_64_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, true); + } + ABORT("invalid compact unwind encoding"); +} + + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A& addressSpace, Registers_x86_64& registers) +{ + uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getRBP() - 8*savedRegistersOffset; + for (int i=0; i < 5; ++i) { + int readerr = 0; + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters, readerr)); + break; + default: + DEBUG_MESSAGE("bad register for RBP frame, encoding=%08X for function starting at 0x%llX\n", compactEncoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + // Error reading memory while doing a remote unwind? + if (readerr) + return UNW_STEP_END; + + savedRegisters += 8; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding, uint64_t functionStart, + A& addressSpace, Registers_x86_64& registers, bool indirectStackSize) +{ + uint32_t stackSizeEncoded = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded*8; + if ( indirectStackSize ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart+stackSizeEncoded); + stackSize = subl + 8*stackAdjust; + } + // decompress permutation + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8*regCount; + for (uint32_t i=0; i < regCount; ++i) { + switch ( registersSaved[i] ) { + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_RBP: + registers.setRBP(addressSpace.get64(savedRegisters)); + break; + default: + DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%llX\n", encoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + +#if SUPPORT_OLD_BINARIES +template +int CompactUnwinder_x86_64::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) +{ + uint64_t savedRegisters; + uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_SIZE); + uint64_t stackSize; + uint32_t stackAdjust; + + switch (compactEncoding & UNWIND_X86_64_CASE_MASK ) { + case UNWIND_X86_64_UNWIND_INFO_UNSPECIFIED: + return UNW_ENOINFO; + + case UNWIND_X86_64_RBP_FRAME_NO_REGS: + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX: + savedRegisters = registers.getRBP() - 8*1; + registers.setRBX(addressSpace.get64(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12: + savedRegisters = registers.getRBP() - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13: + savedRegisters = registers.getRBP() - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14: + savedRegisters = registers.getRBP() - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14_R15: + savedRegisters = registers.getRBP() - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + registers.setR15(addressSpace.get64(savedRegisters+32)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_NO_REGS: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*0; + framelessUnwind(addressSpace, savedRegisters+8*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*1; + registers.setRBX(addressSpace.get64(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+8*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12_R13: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14_R15: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + registers.setR15(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14_R15: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*6; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + registers.setR15(addressSpace.get64(savedRegisters+40)); + framelessUnwind(addressSpace, savedRegisters+8*6, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_NO_REGS: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*0; + framelessUnwind(addressSpace, savedRegisters+8*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*1; + registers.setRBX(addressSpace.get64(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+8*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12_R13: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14_R15: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + registers.setR15(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14_R15: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*6; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + registers.setR15(addressSpace.get64(savedRegisters+40)); + framelessUnwind(addressSpace, savedRegisters+8*6, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + default: + DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%llX\n", + compactEncoding & UNWIND_X86_64_CASE_MASK, functionStart); + ABORT("unknown compact unwind encoding"); + } + return UNW_EINVAL; +} +#endif // SUPPORT_OLD_BINARIES + + +template +void CompactUnwinder_x86_64::frameUnwind(A& addressSpace, Registers_x86_64& registers) +{ + uint64_t rbp = registers.getRBP(); + // ebp points to old ebp + registers.setRBP(addressSpace.get64(rbp)); + // old esp is ebp less saved ebp and return address + registers.setSP(rbp+16); + // pop return address into eip + registers.setIP(addressSpace.get64(rbp+8)); +} + +template +void CompactUnwinder_x86_64::framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers) +{ + // return address is on stack after last saved register + registers.setIP(addressSpace.get64(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation+8); +} + + +}; // namespace lldb_private + + + +#endif // __COMPACT_UNWINDER_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp new file mode 100644 index 000000000000..589c30b50b90 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp @@ -0,0 +1,1686 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- DwarfInstructions.hpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "dwarf2.h" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "InternalMacros.h" +//#include "CompactUnwinder.hpp" + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + +#define CFI_INVALID_ADDRESS ((pint_t)(-1)) + +namespace lldb_private { + +/// +/// Used by linker when parsing __eh_frame section +/// +template +struct CFI_Reference { + typedef typename A::pint_t pint_t; + uint8_t encodingOfTargetAddress; + uint32_t offsetInCFI; + pint_t targetAddress; +}; +template +struct CFI_Atom_Info { + typedef typename A::pint_t pint_t; + pint_t address; + uint32_t size; + bool isCIE; + union { + struct { + CFI_Reference function; + CFI_Reference cie; + CFI_Reference lsda; + uint32_t compactUnwindInfo; + } fdeInfo; + struct { + CFI_Reference personality; + } cieInfo; + } u; +}; + +typedef void (*WarnFunc)(void* ref, uint64_t funcAddr, const char* msg); + +/// +/// DwarfInstructions maps abtract dwarf unwind instructions to a particular architecture +/// +template +class DwarfInstructions +{ +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn); + + + static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]); + + static int stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + static pint_t evaluateExpression(pint_t expression, A& addressSpace, const R& registers, pint_t initialStackValue); + static pint_t getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + static double getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + static v128 getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + + // x86 specific variants + static int lastRestoreReg(const Registers_x86&); + static bool isReturnAddressRegister(int regNum, const Registers_x86&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_x86&); + + static uint32_t getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + + // x86_64 specific variants + static int lastRestoreReg(const Registers_x86_64&); + static bool isReturnAddressRegister(int regNum, const Registers_x86_64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_x86_64&); + + static uint32_t getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86_64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + + // ppc specific variants + static int lastRestoreReg(const Registers_ppc&); + static bool isReturnAddressRegister(int regNum, const Registers_ppc&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_ppc&); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); +}; + + + + +template +const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn) +{ + typename CFI_Parser::CIE_Info cieInfo; + CFI_Atom_Info* entry = infos; + CFI_Atom_Info* end = &infos[infosCount]; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + pint_t currentCFI = p; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return NULL; // end marker + if ( entry >= end ) + return "too little space allocated for parseCFIs"; + pint_t nextCFI = p + cfiLength; + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + const char* err = CFI_Parser::parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = true; + entry->u.cieInfo.personality.targetAddress = cieInfo.personality; + entry->u.cieInfo.personality.offsetInCFI = cieInfo.personalityOffsetInCIE; + entry->u.cieInfo.personality.encodingOfTargetAddress = cieInfo.personalityEncoding; + ++entry; + } + else { + // is FDE + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = false; + entry->u.fdeInfo.function.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.cie.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.lsda.targetAddress = CFI_INVALID_ADDRESS; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) + return "FDE points to CIE outside __eh_frame section"; + // optimize usual case where cie is same for all FDEs + if ( cieStart != cieInfo.cieStart ) { + const char* err = CFI_Parser::parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + } + entry->u.fdeInfo.cie.targetAddress = cieStart; + entry->u.fdeInfo.cie.offsetInCFI = p-currentCFI; + entry->u.fdeInfo.cie.encodingOfTargetAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; + p += 4; + // parse pc begin and range + pint_t offsetOfFunctionAddress = p-currentCFI; + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + entry->u.fdeInfo.function.targetAddress = pcStart; + entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; + entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; + // check for augmentation length + if ( cieInfo.fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo.lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + pint_t offsetOfLSDAAddress = p-currentCFI; + entry->u.fdeInfo.lsda.targetAddress = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry->u.fdeInfo.lsda.offsetInCFI = offsetOfLSDAAddress; + entry->u.fdeInfo.lsda.encodingOfTargetAddress = cieInfo.lsdaEncoding; + } + } + p = endOfAug; + } + // compute compact unwind encoding + typename CFI_Parser::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + ++entry; + } + p = nextCFI; + } + if ( entry != end ) + return "wrong entry count for parseCFIs"; + return NULL; // success +} + + + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]) +{ + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + *lsda = fdeInfo.lsda; + *personality = cieInfo.personality; + compact_unwind_encoding_t encoding; + encoding = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != 0 ) + encoding |= UNWIND_HAS_LSDA; + return encoding; + } + else { + strcpy(warningBuffer, "dwarf unwind instructions could not be parsed"); + return encodeToUseDwarf(dummy); + } + } + else { + strcpy(warningBuffer, "dwarf FDE could not be parsed"); + return encodeToUseDwarf(dummy); + } +} + + +template +typename A::pint_t DwarfInstructions::getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getP(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getP(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + return evaluateExpression(savedReg.value, addressSpace, registers, cfa); + + case CFI_Parser::kRegisterInRegister: + return registers.getRegister(savedReg.value); + + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + ABORT("unsupported restore location for register"); +} + +template +double DwarfInstructions::getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getDouble(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getDouble(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for float register"); +} + +template +v128 DwarfInstructions::getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getVector(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getVector(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for vector register"); +} + + +template +int DwarfInstructions::stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers) +{ + //fprintf(stderr, "stepWithDwarf(pc=0x%0llX, fdeStart=0x%0llX)\n", (uint64_t)pc, (uint64_t)fdeStart); + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if ( CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + R newRegisters = registers; + + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that dwarf says were saved + pint_t returnAddress = 0; + for (int i=0; i <= lastRestoreReg(newRegisters); ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( registers.validFloatRegister(i) ) + newRegisters.setFloatRegister(i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( registers.validVectorRegister(i) ) + newRegisters.setVectorRegister(i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( isReturnAddressRegister(i, registers) ) + returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]); + else if ( registers.validRegister(i) ) + newRegisters.setRegister(i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else + return UNW_EBADREG; + } + } + + // by definition the CFA is the stack pointer at the call site, so restoring SP means setting it to CFA + newRegisters.setSP(cfa); + + // return address is address after call site instruction, so setting IP to that does a return + newRegisters.setIP(returnAddress); + + // do the actual step by replacing the register set with the new ones + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + + + +template +typename A::pint_t DwarfInstructions::evaluateExpression(pint_t expression, A& addressSpace, + const R& registers, pint_t initialStackValue) +{ + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression+20; // just need something until length is read + uint64_t length = addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) fprintf(stderr, "evaluateExpression(): length=%llu\n", length); + pint_t stack[100]; + pint_t* sp = stack; + *(++sp) = initialStackValue; + + while ( p < expressionEnd ) { + if (log) { + for(pint_t* t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) fprintf(stderr, "dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t)addressSpace.get8(p); + p += 1; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t)addressSpace.get16(p); + p += 2; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (int32_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((uint64_t*)value); + if (log) fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = *sp; + if ( svalue < 0 ) + *sp = -svalue; + if (log) fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = *sp--; + *sp = *sp / svalue; + if (log) fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + svalue = *sp--; + *sp = *sp - svalue; + if (log) fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = *sp--; + *sp = *sp % svalue; + if (log) fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = *sp--; + *sp = *sp * svalue; + if (log) fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = *sp; + *sp = ~svalue; + if (log) fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += addressSpace.getULEB128(p, expressionEnd); + if (log) fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = *sp; + *sp = svalue >> value; + if (log) fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + p += svalue; + if (log) fprintf(stderr, "skip %lld\n", (uint64_t)svalue); + break; + + case DW_OP_bra: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + if ( *sp-- ) + p += svalue; + if (log) fprintf(stderr, "bra %lld\n", (uint64_t)svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) fprintf(stderr, "ne\n"); + break; + + 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: + value = opcode - DW_OP_lit0; + *(++sp) = value; + if (log) fprintf(stderr, "push literal 0x%llX\n", (uint64_t)value); + break; + + 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 = opcode - DW_OP_reg0; + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + 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: + reg = opcode - DW_OP_breg0; + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_bregx: + reg = addressSpace.getULEB128(p, expressionEnd); + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_fbreg: + ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch ( addressSpace.get8(p++) ) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = addressSpace.get64(value); + break; + default: + ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + ABORT("dwarf opcode not implemented"); + } + + } + if (log) fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t)*sp); + return *sp; +} + + + +// +// x86_64 specific functions +// + +template +int DwarfInstructions::lastRestoreReg(const Registers_x86_64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)DW_X86_64_RET_ADDR ); + return DW_X86_64_RET_ADDR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_x86_64&) +{ + return (regNum == DW_X86_64_RET_ADDR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_x86_64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86_64 cfa"); +} + + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_x86_64&) +{ + return UNWIND_X86_64_MODE_DWARF; +} + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_x86&) +{ + return UNWIND_X86_MODE_DWARF; +} + + + +template +uint32_t DwarfInstructions::getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/8; + + switch ( reg ) { + case UNW_X86_64_RBX: + return UNWIND_X86_64_REG_RBX << (slotIndex*3); + case UNW_X86_64_R12: + return UNWIND_X86_64_REG_R12 << (slotIndex*3); + case UNW_X86_64_R13: + return UNWIND_X86_64_REG_R13 << (slotIndex*3); + case UNW_X86_64_R14: + return UNWIND_X86_64_REG_R14 << (slotIndex*3); + case UNW_X86_64_R15: + return UNWIND_X86_64_REG_R15 << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.cfaOffsetWasNegative ) { + strcpy(warningBuffer, "cfa had negative offset (dwarf might contain epilog)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_64_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardRBPframe = ( + (prolog.cfaRegister == UNW_X86_64_RBP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_X86_64_RBP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) ); + bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP); + if ( !standardRBPframe && !standardRSPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use RBP or RSP based frame"); + return UNWIND_X86_64_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool rbxSaved = false; + bool r12Saved = false; + bool r13Saved = false; + bool r14Saved = false; + bool r15Saved = false; + bool rbpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_64_MODE_DWARF; + } + switch (i) { + case UNW_X86_64_RBX: + rbxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R12: + r12Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R13: + r13Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R14: + r14Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R15: + r15Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_RBP: + rbpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_64_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_64_MODE_DWARF; + } + } + } + const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value; + const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value; + const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value; + const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value; + const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value; + const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardRBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | rbp | + // +--------------+ <- rbp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+16 + // | saved reg2 | + // +--------------+ <- CFA - offset+8 + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- rsp + // + encoding = UNWIND_X86_64_MODE_RBP_FRAME; + + // find save location of farthest register from rbp + int furthestCfaOffset = 0; + if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetRBX; + if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR12; + if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR13; + if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR14; + if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR15; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int rbpOffset = furthestCfaOffset + 16; + int encodedOffset = rbpOffset/(-8); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( rbxSaved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_RBX, cfaOffsetRBX - furthestCfaOffset, encodingFailure); + if ( r12Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R12, cfaOffsetR12 - furthestCfaOffset, encodingFailure); + if ( r13Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R13, cfaOffsetR13 - furthestCfaOffset, encodingFailure); + if ( r14Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R14, cfaOffsetR14 - furthestCfaOffset, encodingFailure); + if ( r15Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R15, cfaOffsetR15 - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_64_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 16 + // | saved reg2 | + // +--------------+ <- CFA - 24 + // | saved reg3 | + // +--------------+ <- CFA - 32 + // | saved reg4 | + // +--------------+ <- CFA - 40 + // | saved reg5 | + // +--------------+ <- CFA - 48 + // | saved reg6 | + // +--------------+ <- CFA - 56 + // | | + // <- esp + // + + // for RSP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_64_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 8; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_64_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding = UNWIND_X86_64_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( r15Saved ) { + if ( cfaOffsetR15 < -56 ) { + strcpy(warningBuffer, "r15 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15; + } + if ( r14Saved ) { + if ( cfaOffsetR14 < -56 ) { + strcpy(warningBuffer, "r14 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14; + } + if ( r13Saved ) { + if ( cfaOffsetR13 < -56 ) { + strcpy(warningBuffer, "r13 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13; + } + if ( r12Saved ) { + if ( cfaOffsetR12 < -56 ) { + strcpy(warningBuffer, "r12 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12; + } + if ( rbxSaved ) { + if ( cfaOffsetRBX < -56 ) { + strcpy(warningBuffer, "rbx is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX; + } + if ( rbpSaved ) { + if ( cfaOffsetRBP < -56 ) { + strcpy(warningBuffer, "rbp is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_64_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + +// +// x86 specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_x86&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)DW_X86_RET_ADDR ); + return DW_X86_RET_ADDR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_x86&) +{ + return (regNum == DW_X86_RET_ADDR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_x86& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86 cfa"); +} + + + + + +template +uint32_t DwarfInstructions::getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/4; + + switch ( reg ) { + case UNW_X86_EBX: + return UNWIND_X86_REG_EBX << (slotIndex*3); + case UNW_X86_ECX: + return UNWIND_X86_REG_ECX << (slotIndex*3); + case UNW_X86_EDX: + return UNWIND_X86_REG_EDX << (slotIndex*3); + case UNW_X86_EDI: + return UNWIND_X86_REG_EDI << (slotIndex*3); + case UNW_X86_ESI: + return UNWIND_X86_REG_ESI << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardEBPframe = ( + (prolog.cfaRegister == UNW_X86_EBP) + && (prolog.cfaRegisterOffset == 8) + && (prolog.savedRegisters[UNW_X86_EBP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_EBP].value == -8) ); + bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP); + if ( !standardEBPframe && !standardESPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use EBP or ESP based frame"); + return UNWIND_X86_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool ebxSaved = false; + bool ecxSaved = false; + bool edxSaved = false; + bool esiSaved = false; + bool ediSaved = false; + bool ebpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_MODE_DWARF; + } + switch (i) { + case UNW_X86_EBX: + ebxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ECX: + ecxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDX: + edxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ESI: + esiSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDI: + ediSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EBP: + ebpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_MODE_DWARF; + } + } + } + const int32_t cfaOffsetEBX = prolog.savedRegisters[UNW_X86_EBX].value; + const int32_t cfaOffsetECX = prolog.savedRegisters[UNW_X86_ECX].value; + const int32_t cfaOffsetEDX = prolog.savedRegisters[UNW_X86_EDX].value; + const int32_t cfaOffsetEDI = prolog.savedRegisters[UNW_X86_EDI].value; + const int32_t cfaOffsetESI = prolog.savedRegisters[UNW_X86_ESI].value; + const int32_t cfaOffsetEBP = prolog.savedRegisters[UNW_X86_EBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardEBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | ebp | + // +--------------+ <- ebp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+8 + // | saved reg2 | + // +--------------+ <- CFA - offset+e + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- esp + // + encoding = UNWIND_X86_MODE_EBP_FRAME; + + // find save location of farthest register from ebp + int furthestCfaOffset = 0; + if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEBX; + if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetECX; + if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDX; + if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDI; + if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetESI; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int ebpOffset = furthestCfaOffset + 8; + int encodedOffset = ebpOffset/(-4); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( ebxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure); + if ( ecxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure); + if ( edxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure); + if ( ediSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure); + if ( esiSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 8 + // | saved reg2 | + // +--------------+ <- CFA - 12 + // | saved reg3 | + // +--------------+ <- CFA - 16 + // | saved reg4 | + // +--------------+ <- CFA - 20 + // | saved reg5 | + // +--------------+ <- CFA - 24 + // | saved reg6 | + // +--------------+ <- CFA - 28 + // | | + // <- esp + // + + // for ESP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 4; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_MODE_DWARF; + } + encoding = UNWIND_X86_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( ebxSaved ) { + if ( cfaOffsetEBX < -28 ) { + strcpy(warningBuffer, "ebx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX; + } + if ( ecxSaved ) { + if ( cfaOffsetECX < -28 ) { + strcpy(warningBuffer, "ecx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX; + } + if ( edxSaved ) { + if ( cfaOffsetEDX < -28 ) { + strcpy(warningBuffer, "edx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX; + } + if ( ediSaved ) { + if ( cfaOffsetEDI < -28 ) { + strcpy(warningBuffer, "edi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI; + } + if ( esiSaved ) { + if ( cfaOffsetESI < -28 ) { + strcpy(warningBuffer, "esi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI; + } + if ( ebpSaved ) { + if ( cfaOffsetEBP < -28 ) { + strcpy(warningBuffer, "ebp is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + + + + +// +// ppc specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_ppc&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR ); + return UNW_PPC_SPEFSCR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_ppc&) +{ + return (regNum == UNW_PPC_LR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_ppc& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for ppc cfa"); +} + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_ppc&) +{ + return UNWIND_X86_MODE_DWARF; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + return UNWIND_X86_MODE_DWARF; +} + + + + +} // namespace lldb_private + + +#endif // __DWARF_INSTRUCTIONS_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp new file mode 100644 index 000000000000..b11cb8ce4411 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp @@ -0,0 +1,869 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- DwarfParser.hpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ + +#include +#include +#include + +#include + +#include "libunwind.h" +#include "dwarf2.h" + +#include "AddressSpace.hpp" +#include "RemoteUnwindProfile.h" + +namespace lldb_private { + + +/// +/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. +/// See Dwarf Spec for details: +/// http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template +class CFI_Parser +{ +public: + typedef typename A::pint_t pint_t; + + /// + /// Information encoded in a CIE (Common Information Entry) + /// + struct CIE_Info { + pint_t cieStart; + pint_t cieLength; + pint_t cieInstructions; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + pint_t personality; + int codeAlignFactor; + int dataAlignFactor; + bool isSignalFrame; + bool fdesHaveAugmentationData; + }; + + /// + /// Information about an FDE (Frame Description Entry) + /// + struct FDE_Info { + pint_t fdeStart; + pint_t fdeLength; + pint_t fdeInstructions; + pint_t pcStart; + pint_t pcEnd; + pint_t lsda; + }; + + /// + /// Used by linker when parsing __eh_frame section + /// + struct FDE_Reference { + pint_t address; + uint32_t offsetInFDE; + uint8_t encodingOfAddress; + }; + struct FDE_Atom_Info { + pint_t fdeAddress; + FDE_Reference function; + FDE_Reference cie; + FDE_Reference lsda; + }; + struct CIE_Atom_Info { + pint_t cieAddress; + FDE_Reference personality; + }; + + + /// + /// Information about a frame layout and registers saved determined + /// by "running" the dwarf FDE "instructions" + /// + enum { kMaxRegisterNumber = 120 }; + enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA, + kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ; + struct RegisterLocation { + RegisterSavedWhere location; + int64_t value; + }; + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + bool registersInOtherRegisters; + bool registerSavedMoreThanOnce; + bool cfaOffsetWasNegative; + uint32_t spExtraArgSize; + uint32_t codeOffsetAtStackDecrement; + + RegisterLocation savedRegisters[kMaxRegisterNumber]; // from where to restore registers + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i) + : next(n), info(i) {} + PrologInfoStackEntry* next; + PrologInfo info; + }; + + static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo); + +#if defined (SUPPORT_REMOTE_UNWINDING) + static bool functionFuncBoundsViaFDE(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, std::vector &funcbounds); +#endif + + static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo); + static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results); + static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector& fdes, std::vector& cies); + static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength); + + static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo); + +private: + static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, + pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results); + +}; + + +/// +/// Parse a FDE into a CIE_Info and an FDE_Info +/// +template +const char* CFI_Parser::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo) +{ + pint_t p = fdeStart; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return "FDE has zero length"; // end marker + uint32_t ciePointer = addressSpace.get32(p); + if ( ciePointer == 0 ) + return "FDE is really a CIE"; // this is a CIE not an FDE + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p-ciePointer; + const char* err = parseCIE(addressSpace, cieStart, cieInfo); + if (err != NULL) + return err; + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if ( cieInfo->fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo->lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart+pcRange; + return NULL; // success +} + + +/// +/// Scan an eh_frame section to find an FDE for a pc +/// +template +bool CFI_Parser::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo) +{ + //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); + pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + while ( p < ehSectionEnd ) { + pint_t currentCFI = p; + //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // skip over CIEs + p += cfiLength; + } + else { + // process FDE to see if it covers pc + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) { + if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) { + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) { + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if ( cieInfo->fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo->lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = currentCFI; + fdeInfo->fdeLength = nextCFI - currentCFI; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart+pcRange; + //fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + return true; + } + else { + //fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // pc is not in begin/range, skip this FDE + } + } + else { + // malformed CIE, now augmentation describing pc range encoding + //fprintf(stderr, "malformed CIE\n"); + } + } + else { + // malformed FDE. CIE is bad + //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n", + // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd); + } + p = nextCFI; + } + } + //fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc); + return false; +} + +#if defined (SUPPORT_REMOTE_UNWINDING) +/// Scan an eh_frame section to find all the function start addresses +/// This is only made for working with libunwind-remote. It copies +/// the eh_frame section into local memory and steps through it quickly +/// to find the start addresses of the CFIs. +/// +template +bool CFI_Parser::functionFuncBoundsViaFDE(A& addressSpace, pint_t ehSectionStart, + uint32_t sectionLength, std::vector &funcbounds) +{ + //fprintf(stderr, "functionFuncBoundsViaFDE(0x%llX)\n", (long long)pc); + pint_t p = ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + pint_t lastCieSeen = (pint_t) -1; + CIE_Info cieInfo; + while ( p < ehSectionEnd ) { + //fprintf(stderr, "functionFuncBoundsViaFDE() CFI at 0x%llX\n", (long long)p); + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // skip over CIEs + p += cfiLength; + } + else { + // process FDE to see if it covers pc + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) { + const char *errmsg; + // don't re-parse the cie if this fde is pointing to one we already parsed + if (cieStart == lastCieSeen) { + errmsg = NULL; + } + else { + errmsg = parseCIE(addressSpace, cieStart, &cieInfo); + if (errmsg == NULL) + lastCieSeen = cieStart; + } + if ( errmsg == NULL ) { + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + funcbounds.push_back(FuncBounds(pcStart, pcStart + pcRange)); + } + else { + // malformed CIE, now augmentation describing pc range encoding + //fprintf(stderr, "malformed CIE\n"); + return false; + } + } + else { + // malformed FDE. CIE is bad + //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n", + // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd); + return false; + } + p = nextCFI; + } + } + return true; +} +#endif // SUPPORT_REMOTE_UNWINDING + + + +/// +/// Extract info from a CIE +/// +template +const char* CFI_Parser::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo) +{ + //fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie); + cieInfo->pointerEncoding = 0; + cieInfo->lsdaEncoding = 0; + cieInfo->personalityEncoding = 0; + cieInfo->personalityOffsetInCIE = 0; + cieInfo->personality = 0; + cieInfo->codeAlignFactor = 0; + cieInfo->dataAlignFactor = 0; + cieInfo->isSignalFrame = false; + cieInfo->fdesHaveAugmentationData = false; + cieInfo->cieStart = cie; + pint_t p = cie; + uint64_t cieLength = addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if ( cieLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cieLength = addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if ( cieLength == 0 ) + return false; + // CIE ID is always 0 + if ( addressSpace.get32(p) != 0 ) + return "CIE ID is not zero"; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if ( (version != 1) && (version != 3) ) + return "CIE version is not 1 or 3"; + ++p; + // save start of augmentation string and find end + pint_t strStart = p; + while ( addressSpace.get8(p) != 0 ) + ++p; + ++p; + // parse code aligment factor + cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd); + // parse data alignment factor + cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd); + // parse return address register + addressSpace.getULEB128(p, cieContentEnd); + // parse augmentation data based on augmentation string + const char* result = NULL; + if ( addressSpace.get8(strStart) == 'z' ) { + // parse augmentation data length + addressSpace.getULEB128(p, cieContentEnd); + for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) { + switch ( addressSpace.get8(s) ) { + case 'z': + cieInfo->fdesHaveAugmentationData = true; + break; + case 'P': + cieInfo->personalityEncoding = addressSpace.get8(p); + ++p; + cieInfo->personalityOffsetInCIE = p-cie; + cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + break; + case 'L': + cieInfo->lsdaEncoding = addressSpace.get8(p); + ++p; + break; + case 'R': + cieInfo->pointerEncoding = addressSpace.get8(p); + ++p; + break; + case 'S': + cieInfo->isSignalFrame = true; + break; + default: + // ignore unknown letters + break; + } + } + } + cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; + cieInfo->cieInstructions = p; + return result; +} + + +template +uint32_t CFI_Parser::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength) +{ + uint32_t count = 0; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return count; // end marker + ++count; + p += cfiLength; + } + return count; +} + + + +template +const char* CFI_Parser::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector& fdes, std::vector& cies) +{ + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + pint_t currentCFI = p; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return NULL; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + CIE_Info cieInfo; + const char* err = parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + CIE_Atom_Info entry; + entry.cieAddress = currentCFI; + entry.personality.address = cieInfo.personality; + entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE; + entry.personality.encodingOfAddress = cieInfo.personalityEncoding; + cies.push_back(entry); + p += cfiLength; + } + else { + // is FDE + FDE_Atom_Info entry; + entry.fdeAddress = currentCFI; + entry.function.address = 0; + entry.cie.address = 0; + entry.lsda.address = 0; + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) + return "FDE points to CIE outside __eh_frame section"; + CIE_Info cieInfo; + const char* err = parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + entry.cie.address = cieStart; + entry.cie.offsetInFDE = p-currentCFI; + entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; + p += 4; + // parse pc begin and range + pint_t offsetOfFunctionAddress = p-currentCFI; + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + entry.function.address = pcStart; + entry.function.offsetInFDE = offsetOfFunctionAddress; + entry.function.encodingOfAddress = cieInfo.pointerEncoding; + // skip over augmentation length + if ( cieInfo.fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) { + pint_t offsetOfLSDAAddress = p-currentCFI; + entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry.lsda.offsetInFDE = offsetOfLSDAAddress; + entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding; + } + p = endOfAug; + } + fdes.push_back(entry); + p = nextCFI; + } + } + return NULL; // success +} + + + +/// +/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE +/// +template +bool CFI_Parser::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results) +{ + // clear results + bzero(results, sizeof(PrologInfo)); + PrologInfoStackEntry* rememberStack = NULL; + + // parse CIE then FDE instructions + return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength, + cieInfo, (pint_t)(-1), rememberStack, results) + && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength, + cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results); +} + + +/// +/// "run" the dwarf instructions +/// +template +bool CFI_Parser::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, + pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results) +{ + const bool logDwarf = false; + pint_t p = instructions; + uint32_t codeOffset = 0; + PrologInfo initialState = *results; + + // see Dwarf Spec, section 6.4.2 for details on unwind opcodes + while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) { + uint64_t reg; + uint64_t reg2; + int64_t offset; + uint64_t length; + uint8_t opcode = addressSpace.get8(p); + uint8_t operand; + PrologInfoStackEntry* entry; + ++p; + switch (opcode) { + case DW_CFA_nop: + if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); + if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n"); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset); + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset); + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset); + break; + case DW_CFA_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n"); + return false; + } + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_restore_extended: + reg = addressSpace.getULEB128(p, instructionsEnd);; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg); + break; + case DW_CFA_undefined: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterUnused; + if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg); + break; + case DW_CFA_same_value: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n"); + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg); + break; + case DW_CFA_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + reg2 = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n"); + return false; + } + if ( reg2 > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = reg2; + results->registersInOtherRegisters = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2); + break; + case DW_CFA_remember_state: + // avoid operator new, because that would be an upward dependency + entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry)); + if ( entry != NULL ) { + entry->next = rememberStack; + entry->info = *results; + rememberStack = entry; + } + else { + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n"); + break; + case DW_CFA_restore_state: + if ( rememberStack != NULL ) { + PrologInfoStackEntry* top = rememberStack; + *results = top->info; + rememberStack = top->next; + free((char*)top); + } + else { + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n"); + break; + case DW_CFA_def_cfa: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + if ( offset > 0x80000000 ) + results->cfaOffsetWasNegative = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg); + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd); + results->codeOffsetAtStackDecrement = codeOffset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset); + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n", + results->cfaExpression, length); + break; + case DW_CFA_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterAtExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_offset_extended_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->codeOffsetAtStackDecrement = codeOffset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset); + break; + case DW_CFA_val_offset: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset); + break; + case DW_CFA_val_offset_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset); + break; + case DW_CFA_val_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterIsExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_GNU_args_size: + offset = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset); + break; + case DW_CFA_GNU_negative_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = -offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset); + break; + default: + operand = opcode & 0x3F; + switch ( opcode & 0xC0 ) { + case DW_CFA_offset: + reg = operand; + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset); + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset); + break; + case DW_CFA_restore: + // Python crashes when handling an exception thrown by an obj-c object + // libffi uses DW_CFA_restore in the middle of some custom dward, so it is not a good epilog flag + //return true; // gcc-4.5 starts the epilog with this + reg = operand; + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg); + break; + default: + if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + + return true; +} + + +} // namespace lldb_private + + +#endif // __DWARF_PARSER_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp new file mode 100644 index 000000000000..a4af02051a57 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp @@ -0,0 +1,135 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- FileAbstraction.hpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef int32_t int_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef int64_t int_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } +}; + + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h b/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h new file mode 100644 index 000000000000..40483900d38a --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h @@ -0,0 +1,89 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- InternalMacros.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef INTERNAL_MACROS_H +#define INTERNAL_MACROS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern void __assert_rtn(const char *, const char *, int, const char *) __attribute__((noreturn)); +#ifdef __cplusplus +} +#endif + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + + +struct v128 { unsigned int vec[4]; }; + + +#define EXPORT __attribute__((visibility("default"))) + +#define COMPILE_TIME_ASSERT( expr ) \ + extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) ); + +#define ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg) + +#if NDEBUG + #define DEBUG_MESSAGE(msg, ...) + #define DEBUG_PRINT_API(msg, ...) + #define DEBUG_PRINT_UNWINDING_TEST 0 + #define DEBUG_PRINT_UNWINDING(msg, ...) + #define DEBUG_LOG_NON_ZERO(x) x; + #define INITIALIZE_DEBUG_PRINT_API + #define INITIALIZE_DEBUG_PRINT_UNWINDING +#else + #define DEBUG_MESSAGE(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__) + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + #ifdef __cplusplus + } + #endif + #define DEBUG_LOG_NON_ZERO(x) { int _err = x; if ( _err != 0 ) fprintf(stderr, "libuwind: " #x "=%d in %s", _err, __FUNCTION__); } + #define DEBUG_PRINT_API(msg, ...) do { if ( logAPIs() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING(msg, ...) do { if ( logUnwinding() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING_TEST logUnwinding() + #define INITIALIZE_DEBUG_PRINT_API bool logAPIs() { static bool log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); return log; } + #define INITIALIZE_DEBUG_PRINT_UNWINDING bool logUnwinding() { static bool log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); return log; } +#endif + + +// note hack for +// Once libgcc_s.dylib vectors to libSystem, then we can remove the $ld$hide$os10.6$ lines +#if __ppc__ + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + + +#endif // INTERNAL_MACROS_H diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp new file mode 100644 index 000000000000..291c72425d4a --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp @@ -0,0 +1,985 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- Registers.hpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libunwind.h" +#include "InternalMacros.h" + +namespace lldb_private { + + +/// +/// Registers_x86 holds the register state of a thread in a 32-bit intel process. +/// +class Registers_x86 +{ +public: + Registers_x86(); + Registers_x86(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto() {} + + uint32_t getSP() const { return fRegisters.__esp; } + void setSP(uint32_t value) { fRegisters.__esp = value; } + uint32_t getIP() const { return fRegisters.__eip; } + void setIP(uint32_t value) { fRegisters.__eip = value; } + uint32_t getEBP() const { return fRegisters.__ebp; } + void setEBP(uint32_t value) { fRegisters.__ebp = value; } + uint32_t getEBX() const { return fRegisters.__ebx; } + void setEBX(uint32_t value) { fRegisters.__ebx = value; } + uint32_t getECX() const { return fRegisters.__ecx; } + void setECX(uint32_t value) { fRegisters.__ecx = value; } + uint32_t getEDX() const { return fRegisters.__edx; } + void setEDX(uint32_t value) { fRegisters.__edx = value; } + uint32_t getESI() const { return fRegisters.__esi; } + void setESI(uint32_t value) { fRegisters.__esi = value; } + uint32_t getEDI() const { return fRegisters.__edi; } + void setEDI(uint32_t value) { fRegisters.__edi = value; } + +private: + i386_thread_state_t fRegisters; +}; + +inline Registers_x86::Registers_x86(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86) < sizeof(unw_context_t) ); + fRegisters = *((i386_thread_state_t*)registers); +} + +inline Registers_x86::Registers_x86() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 7 ) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__eip; + case UNW_REG_SP: + return fRegisters.__esp; + case UNW_X86_EAX: + return fRegisters.__eax; + case UNW_X86_ECX: + return fRegisters.__ecx; + case UNW_X86_EDX: + return fRegisters.__edx; + case UNW_X86_EBX: + return fRegisters.__ebx; + case UNW_X86_EBP: + return fRegisters.__ebp; + case UNW_X86_ESP: + return fRegisters.__esp; + case UNW_X86_ESI: + return fRegisters.__esi; + case UNW_X86_EDI: + return fRegisters.__edi; + } + ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__eip = value; + return; + case UNW_REG_SP: + fRegisters.__esp = value; + return; + case UNW_X86_EAX: + fRegisters.__eax = value; + return; + case UNW_X86_ECX: + fRegisters.__ecx = value; + return; + case UNW_X86_EDX: + fRegisters.__edx = value; + return; + case UNW_X86_EBX: + fRegisters.__ebx = value; + return; + case UNW_X86_EBP: + fRegisters.__ebp = value; + return; + case UNW_X86_ESP: + fRegisters.__esp = value; + return; + case UNW_X86_ESI: + fRegisters.__esi = value; + return; + case UNW_X86_EDI: + fRegisters.__edi = value; + return; + } + ABORT("unsupported x86 register"); +} + +inline const char* Registers_x86::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int num) const +{ + ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int num, double value) +{ + ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int num) const +{ + ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int num, v128 value) +{ + ABORT("no x86 vector registers"); +} + + + + +/// +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_x86_64 +{ +public: + Registers_x86_64(); + Registers_x86_64(const void* registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const{ return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto() {} + uint64_t getSP() const { return fRegisters.__rsp; } + void setSP(uint64_t value) { fRegisters.__rsp = value; } + uint64_t getIP() const { return fRegisters.__rip; } + void setIP(uint64_t value) { fRegisters.__rip = value; } + uint64_t getRBP() const { return fRegisters.__rbp; } + void setRBP(uint64_t value) { fRegisters.__rbp = value; } + uint64_t getRBX() const { return fRegisters.__rbx; } + void setRBX(uint64_t value) { fRegisters.__rbx = value; } + uint64_t getR12() const { return fRegisters.__r12; } + void setR12(uint64_t value) { fRegisters.__r12 = value; } + uint64_t getR13() const { return fRegisters.__r13; } + void setR13(uint64_t value) { fRegisters.__r13 = value; } + uint64_t getR14() const { return fRegisters.__r14; } + void setR14(uint64_t value) { fRegisters.__r14 = value; } + uint64_t getR15() const { return fRegisters.__r15; } + void setR15(uint64_t value) { fRegisters.__r15 = value; } +private: + x86_thread_state64_t fRegisters; +}; + +inline Registers_x86_64::Registers_x86_64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86_64) < sizeof(unw_context_t) ); + fRegisters = *((x86_thread_state64_t*)registers); +} + +inline Registers_x86_64::Registers_x86_64() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86_64::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 15 ) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__rip; + case UNW_REG_SP: + return fRegisters.__rsp; + case UNW_X86_64_RAX: + return fRegisters.__rax; + case UNW_X86_64_RDX: + return fRegisters.__rdx; + case UNW_X86_64_RCX: + return fRegisters.__rcx; + case UNW_X86_64_RBX: + return fRegisters.__rbx; + case UNW_X86_64_RSI: + return fRegisters.__rsi; + case UNW_X86_64_RDI: + return fRegisters.__rdi; + case UNW_X86_64_RBP: + return fRegisters.__rbp; + case UNW_X86_64_RSP: + return fRegisters.__rsp; + case UNW_X86_64_R8: + return fRegisters.__r8; + case UNW_X86_64_R9: + return fRegisters.__r9; + case UNW_X86_64_R10: + return fRegisters.__r10; + case UNW_X86_64_R11: + return fRegisters.__r11; + case UNW_X86_64_R12: + return fRegisters.__r12; + case UNW_X86_64_R13: + return fRegisters.__r13; + case UNW_X86_64_R14: + return fRegisters.__r14; + case UNW_X86_64_R15: + return fRegisters.__r15; + } + ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__rip = value; + return; + case UNW_REG_SP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_RAX: + fRegisters.__rax = value; + return; + case UNW_X86_64_RDX: + fRegisters.__rdx = value; + return; + case UNW_X86_64_RCX: + fRegisters.__rcx = value; + return; + case UNW_X86_64_RBX: + fRegisters.__rbx = value; + return; + case UNW_X86_64_RSI: + fRegisters.__rsi = value; + return; + case UNW_X86_64_RDI: + fRegisters.__rdi = value; + return; + case UNW_X86_64_RBP: + fRegisters.__rbp = value; + return; + case UNW_X86_64_RSP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_R8: + fRegisters.__r8 = value; + return; + case UNW_X86_64_R9: + fRegisters.__r9 = value; + return; + case UNW_X86_64_R10: + fRegisters.__r10 = value; + return; + case UNW_X86_64_R11: + fRegisters.__r11 = value; + return; + case UNW_X86_64_R12: + fRegisters.__r12 = value; + return; + case UNW_X86_64_R13: + fRegisters.__r13 = value; + return; + case UNW_X86_64_R14: + fRegisters.__r14 = value; + return; + case UNW_X86_64_R15: + fRegisters.__r15 = value; + return; + } + ABORT("unsupported x86_64 register"); +} + +inline const char* Registers_x86_64::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + default: + return "unknown register"; + } +} + +double Registers_x86_64::getFloatRegister(int num) const +{ + ABORT("no x86_64 float registers"); +} + +void Registers_x86_64::setFloatRegister(int num, double value) +{ + ABORT("no x86_64 float registers"); +} + +inline v128 Registers_x86_64::getVectorRegister(int num) const +{ + ABORT("no x86_64 vector registers"); +} + +inline void Registers_x86_64::setVectorRegister(int num, v128 value) +{ + ABORT("no x86_64 vector registers"); +} + + +/// +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC process. +/// +class Registers_ppc +{ +public: + Registers_ppc(); + Registers_ppc(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + void jumpto() {} + const char* getRegisterName(int num); + uint64_t getSP() const { return fRegisters.__r1; } + void setSP(uint64_t value) { fRegisters.__r1 = value; } + uint64_t getIP() const { return fRegisters.__srr0; } + void setIP(uint64_t value) { fRegisters.__srr0 = value; } +private: + ppc_thread_state_t fRegisters; + ppc_float_state_t fFloatRegisters; + v128 fVectorRegisters[32]; // offset 424 +}; + + + +inline Registers_ppc::Registers_ppc(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_ppc) < sizeof(unw_context_t) ); + fRegisters = *((ppc_thread_state_t*)registers); + fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160)); + memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fFloatRegisters, sizeof(fFloatRegisters)); + bzero(&fVectorRegisters, sizeof(fVectorRegisters)); +} + + +inline bool Registers_ppc::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum == UNW_PPC_VRSAVE ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum <= UNW_PPC_R31 ) + return true; + if ( regNum == UNW_PPC_MQ ) + return true; + if ( regNum == UNW_PPC_LR ) + return true; + if ( regNum == UNW_PPC_CTR ) + return true; + if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) ) + return true; + return false; +} + + +inline uint32_t Registers_ppc::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__srr0; + case UNW_REG_SP: + return fRegisters.__r1; + case UNW_PPC_R0: + return fRegisters.__r0; + case UNW_PPC_R1: + return fRegisters.__r1; + case UNW_PPC_R2: + return fRegisters.__r2; + case UNW_PPC_R3: + return fRegisters.__r3; + case UNW_PPC_R4: + return fRegisters.__r4; + case UNW_PPC_R5: + return fRegisters.__r5; + case UNW_PPC_R6: + return fRegisters.__r6; + case UNW_PPC_R7: + return fRegisters.__r7; + case UNW_PPC_R8: + return fRegisters.__r8; + case UNW_PPC_R9: + return fRegisters.__r9; + case UNW_PPC_R10: + return fRegisters.__r10; + case UNW_PPC_R11: + return fRegisters.__r11; + case UNW_PPC_R12: + return fRegisters.__r12; + case UNW_PPC_R13: + return fRegisters.__r13; + case UNW_PPC_R14: + return fRegisters.__r14; + case UNW_PPC_R15: + return fRegisters.__r15; + case UNW_PPC_R16: + return fRegisters.__r16; + case UNW_PPC_R17: + return fRegisters.__r17; + case UNW_PPC_R18: + return fRegisters.__r18; + case UNW_PPC_R19: + return fRegisters.__r19; + case UNW_PPC_R20: + return fRegisters.__r20; + case UNW_PPC_R21: + return fRegisters.__r21; + case UNW_PPC_R22: + return fRegisters.__r22; + case UNW_PPC_R23: + return fRegisters.__r23; + case UNW_PPC_R24: + return fRegisters.__r24; + case UNW_PPC_R25: + return fRegisters.__r25; + case UNW_PPC_R26: + return fRegisters.__r26; + case UNW_PPC_R27: + return fRegisters.__r27; + case UNW_PPC_R28: + return fRegisters.__r28; + case UNW_PPC_R29: + return fRegisters.__r29; + case UNW_PPC_R30: + return fRegisters.__r30; + case UNW_PPC_R31: + return fRegisters.__r31; + case UNW_PPC_LR: + return fRegisters.__lr; + case UNW_PPC_CR0: + return (fRegisters.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (fRegisters.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (fRegisters.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (fRegisters.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (fRegisters.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (fRegisters.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (fRegisters.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (fRegisters.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return fRegisters.__vrsave; + } + ABORT("unsupported ppc register"); +} + + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) +{ + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__srr0 = value; + return; + case UNW_REG_SP: + fRegisters.__r1 = value; + return; + case UNW_PPC_R0: + fRegisters.__r0 = value; + return; + case UNW_PPC_R1: + fRegisters.__r1 = value; + return; + case UNW_PPC_R2: + fRegisters.__r2 = value; + return; + case UNW_PPC_R3: + fRegisters.__r3 = value; + return; + case UNW_PPC_R4: + fRegisters.__r4 = value; + return; + case UNW_PPC_R5: + fRegisters.__r5 = value; + return; + case UNW_PPC_R6: + fRegisters.__r6 = value; + return; + case UNW_PPC_R7: + fRegisters.__r7 = value; + return; + case UNW_PPC_R8: + fRegisters.__r8 = value; + return; + case UNW_PPC_R9: + fRegisters.__r9 = value; + return; + case UNW_PPC_R10: + fRegisters.__r10 = value; + return; + case UNW_PPC_R11: + fRegisters.__r11 = value; + return; + case UNW_PPC_R12: + fRegisters.__r12 = value; + return; + case UNW_PPC_R13: + fRegisters.__r13 = value; + return; + case UNW_PPC_R14: + fRegisters.__r14 = value; + return; + case UNW_PPC_R15: + fRegisters.__r15 = value; + return; + case UNW_PPC_R16: + fRegisters.__r16 = value; + return; + case UNW_PPC_R17: + fRegisters.__r17 = value; + return; + case UNW_PPC_R18: + fRegisters.__r18 = value; + return; + case UNW_PPC_R19: + fRegisters.__r19 = value; + return; + case UNW_PPC_R20: + fRegisters.__r20 = value; + return; + case UNW_PPC_R21: + fRegisters.__r21 = value; + return; + case UNW_PPC_R22: + fRegisters.__r22 = value; + return; + case UNW_PPC_R23: + fRegisters.__r23 = value; + return; + case UNW_PPC_R24: + fRegisters.__r24 = value; + return; + case UNW_PPC_R25: + fRegisters.__r25 = value; + return; + case UNW_PPC_R26: + fRegisters.__r26 = value; + return; + case UNW_PPC_R27: + fRegisters.__r27 = value; + return; + case UNW_PPC_R28: + fRegisters.__r28 = value; + return; + case UNW_PPC_R29: + fRegisters.__r29 = value; + return; + case UNW_PPC_R30: + fRegisters.__r30 = value; + return; + case UNW_PPC_R31: + fRegisters.__r31 = value; + return; + case UNW_PPC_MQ: + fRegisters.__mq = value; + return; + case UNW_PPC_LR: + fRegisters.__lr = value; + return; + case UNW_PPC_CTR: + fRegisters.__ctr = value; + return; + case UNW_PPC_CR0: + fRegisters.__cr &= 0x0FFFFFFF; + fRegisters.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + fRegisters.__cr &= 0xF0FFFFFF; + fRegisters.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + fRegisters.__cr &= 0xFF0FFFFF; + fRegisters.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + fRegisters.__cr &= 0xFFF0FFFF; + fRegisters.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + fRegisters.__cr &= 0xFFFF0FFF; + fRegisters.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + fRegisters.__cr &= 0xFFFFF0FF; + fRegisters.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + fRegisters.__cr &= 0xFFFFFF0F; + fRegisters.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + fRegisters.__cr &= 0xFFFFFFF0; + fRegisters.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + fRegisters.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + fRegisters.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_PPC_F0 ) + return false; + if ( regNum > UNW_PPC_F31 ) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) +{ + //fprintf(stderr, "Registers_ppc::setFloatRegister(%d, %g))\n", regNum, value); + assert(validFloatRegister(regNum)); + fFloatRegisters.__fpregs[regNum-UNW_PPC_F0] = value; +} + + +inline bool Registers_ppc::validVectorRegister(int regNum) const +{ + if ( regNum < UNW_PPC_V0 ) + return false; + if ( regNum > UNW_PPC_V31 ) + return false; + return true; +} + +v128 Registers_ppc::getVectorRegister(int regNum) const +{ + assert(validVectorRegister(regNum)); + v128 result = fVectorRegisters[regNum-UNW_PPC_V0]; + //fprintf(stderr, "Registers_ppc::getVectorRegister(this=%p, %d) => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, result.vec[0], result.vec[1], result.vec[2], result.vec[3]); + return result; +} + +void Registers_ppc::setVectorRegister(int regNum, v128 value) +{ + assert(validVectorRegister(regNum)); + //fprintf(stderr, "Registers_ppc::setVectorRegister(this=%p, %d) <0x%08X, 0x%08X, 0x%08X, 0x%08X> => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, fVectorRegisters[regNum-UNW_PPC_V0].vec[0], fVectorRegisters[regNum-UNW_PPC_V0].vec[1], fVectorRegisters[regNum-UNW_PPC_V0].vec[2], + // fVectorRegisters[regNum-UNW_PPC_V0].vec[3], value.vec[0], value.vec[1], value.vec[2], value.vec[3]); + fVectorRegisters[regNum-UNW_PPC_V0] = value; +} + + +inline const char* Registers_ppc::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + + +} + + +} // namespace lldb_private + + + +#endif // __REGISTERS_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s new file mode 100644 index 000000000000..45dae3bcbfc1 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s @@ -0,0 +1,261 @@ + + +#if __i386__ + .text + .globl __ZN12lldb_private13Registers_x866jumptoEv + .private_extern __ZN12lldb_private13Registers_x866jumptoEv +__ZN12lldb_private13Registers_x866jumptoEv: +# +# void lldb_private::Registers_x86::jumpto() +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + + movl 4(%esp), %eax + # set up eax and ret on new stack location + movl 28(%eax), %edx # edx holds new stack pointer + subl $8,%edx + movl %edx, 28(%eax) + movl 0(%eax), %ebx + movl %ebx, 0(%edx) + movl 40(%eax), %ebx + movl %ebx, 4(%edx) + # we now have ret and eax pushed onto where new stack will be + # restore all registers + movl 4(%eax), %ebx + movl 8(%eax), %ecx + movl 12(%eax), %edx + movl 16(%eax), %edi + movl 20(%eax), %esi + movl 24(%eax), %ebp + movl 28(%eax), %esp + # skip ss + # skip eflags + pop %eax # eax was already pushed on new stack + ret # eip was already pushed on new stack + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + +#elif __x86_64__ + + .text + .globl __ZN12lldb_private16Registers_x86_646jumptoEv + .private_extern __ZN12lldb_private16Registers_x86_646jumptoEv +__ZN12lldb_private16Registers_x86_646jumptoEv: +# +# void lldb_private::Registers_x86_64::jumpto() +# +# On entry, thread_state pointer is in rdi + + movq 56(%rdi), %rax # rax holds new stack pointer + subq $16, %rax + movq %rax, 56(%rdi) + movq 32(%rdi), %rbx # store new rdi on new stack + movq %rbx, 0(%rax) + movq 128(%rdi), %rbx # store new rip on new stack + movq %rbx, 8(%rax) + # restore all registers + movq 0(%rdi), %rax + movq 8(%rdi), %rbx + movq 16(%rdi), %rcx + movq 24(%rdi), %rdx + # restore rdi later + movq 40(%rdi), %rsi + movq 48(%rdi), %rbp + # restore rsp later + movq 64(%rdi), %r8 + movq 72(%rdi), %r9 + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + # skip rflags + # skip cs + # skip fs + # skip gs + movq 56(%rdi), %rsp # cut back rsp to new location + pop %rdi # rdi was saved here earlier + ret # rip was saved here + + +#elif __ppc__ + + .text + .globl __ZN12lldb_private13Registers_ppc6jumptoEv + .private_extern __ZN12lldb_private13Registers_ppc6jumptoEv +__ZN12lldb_private13Registers_ppc6jumptoEv: +; +; void lldb_private::Registers_ppc::jumpto() +; +; On entry: +; thread_state pointer is in r3 +; + + ; restore integral registerrs + ; skip r0 for now + ; skip r1 for now + lwz r2, 16(r3) + ; skip r3 for now + ; skip r4 for now + ; skip r5 for now + lwz r6, 32(r3) + lwz r7, 36(r3) + lwz r8, 40(r3) + lwz r9, 44(r3) + lwz r10, 48(r3) + lwz r11, 52(r3) + lwz r12, 56(r3) + lwz r13, 60(r3) + lwz r14, 64(r3) + lwz r15, 68(r3) + lwz r16, 72(r3) + lwz r17, 76(r3) + lwz r18, 80(r3) + lwz r19, 84(r3) + lwz r20, 88(r3) + lwz r21, 92(r3) + lwz r22, 96(r3) + lwz r23,100(r3) + lwz r24,104(r3) + lwz r25,108(r3) + lwz r26,112(r3) + lwz r27,116(r3) + lwz r28,120(r3) + lwz r29,124(r3) + lwz r30,128(r3) + lwz r31,132(r3) + + ; restore float registers + lfd f0, 160(r3) + lfd f1, 168(r3) + lfd f2, 176(r3) + lfd f3, 184(r3) + lfd f4, 192(r3) + lfd f5, 200(r3) + lfd f6, 208(r3) + lfd f7, 216(r3) + lfd f8, 224(r3) + lfd f9, 232(r3) + lfd f10,240(r3) + lfd f11,248(r3) + lfd f12,256(r3) + lfd f13,264(r3) + lfd f14,272(r3) + lfd f15,280(r3) + lfd f16,288(r3) + lfd f17,296(r3) + lfd f18,304(r3) + lfd f19,312(r3) + lfd f20,320(r3) + lfd f21,328(r3) + lfd f22,336(r3) + lfd f23,344(r3) + lfd f24,352(r3) + lfd f25,360(r3) + lfd f26,368(r3) + lfd f27,376(r3) + lfd f28,384(r3) + lfd f29,392(r3) + lfd f30,400(r3) + lfd f31,408(r3) + + ; restore vector registers if any are in use + lwz r5,156(r3) ; test VRsave + cmpwi r5,0 + beq Lnovec + + subi r4,r1,16 + rlwinm r4,r4,0,0,27 ; mask low 4-bits + ; r4 is now a 16-byte aligned pointer into the red zone + ; the fVectorRegisters may not be 16-byte aligned so copy via red zone temp buffer + + +#define LOAD_VECTOR_UNALIGNEDl(_index) \ + andis. r0,r5,(1<<(15-_index)) @\ + beq Ldone ## _index @\ + lwz r0, 424+_index*16(r3) @\ + stw r0, 0(r4) @\ + lwz r0, 424+_index*16+4(r3) @\ + stw r0, 4(r4) @\ + lwz r0, 424+_index*16+8(r3) @\ + stw r0, 8(r4) @\ + lwz r0, 424+_index*16+12(r3)@\ + stw r0, 12(r4) @\ + lvx v ## _index,0,r4 @\ +Ldone ## _index: + +#define LOAD_VECTOR_UNALIGNEDh(_index) \ + andi. r0,r5,(1<<(31-_index)) @\ + beq Ldone ## _index @\ + lwz r0, 424+_index*16(r3) @\ + stw r0, 0(r4) @\ + lwz r0, 424+_index*16+4(r3) @\ + stw r0, 4(r4) @\ + lwz r0, 424+_index*16+8(r3) @\ + stw r0, 8(r4) @\ + lwz r0, 424+_index*16+12(r3)@\ + stw r0, 12(r4) @\ + lvx v ## _index,0,r4 @\ + Ldone ## _index: + + + LOAD_VECTOR_UNALIGNEDl(0) + LOAD_VECTOR_UNALIGNEDl(1) + LOAD_VECTOR_UNALIGNEDl(2) + LOAD_VECTOR_UNALIGNEDl(3) + LOAD_VECTOR_UNALIGNEDl(4) + LOAD_VECTOR_UNALIGNEDl(5) + LOAD_VECTOR_UNALIGNEDl(6) + LOAD_VECTOR_UNALIGNEDl(7) + LOAD_VECTOR_UNALIGNEDl(8) + LOAD_VECTOR_UNALIGNEDl(9) + LOAD_VECTOR_UNALIGNEDl(10) + LOAD_VECTOR_UNALIGNEDl(11) + LOAD_VECTOR_UNALIGNEDl(12) + LOAD_VECTOR_UNALIGNEDl(13) + LOAD_VECTOR_UNALIGNEDl(14) + LOAD_VECTOR_UNALIGNEDl(15) + LOAD_VECTOR_UNALIGNEDh(16) + LOAD_VECTOR_UNALIGNEDh(17) + LOAD_VECTOR_UNALIGNEDh(18) + LOAD_VECTOR_UNALIGNEDh(19) + LOAD_VECTOR_UNALIGNEDh(20) + LOAD_VECTOR_UNALIGNEDh(21) + LOAD_VECTOR_UNALIGNEDh(22) + LOAD_VECTOR_UNALIGNEDh(23) + LOAD_VECTOR_UNALIGNEDh(24) + LOAD_VECTOR_UNALIGNEDh(25) + LOAD_VECTOR_UNALIGNEDh(26) + LOAD_VECTOR_UNALIGNEDh(27) + LOAD_VECTOR_UNALIGNEDh(28) + LOAD_VECTOR_UNALIGNEDh(29) + LOAD_VECTOR_UNALIGNEDh(30) + LOAD_VECTOR_UNALIGNEDh(31) + +Lnovec: + lwz r0, 136(r3) ; __cr + mtocrf 255,r0 + lwz r0, 148(r3) ; __ctr + mtctr r0 + lwz r0, 0(r3) ; __ssr0 + mtctr r0 + lwz r0, 8(r3) ; do r0 now + lwz r5,28(r3) ; do r5 now + lwz r4,24(r3) ; do r4 now + lwz r1,12(r3) ; do sp now + lwz r3,20(r3) ; do r3 last + bctr + + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp new file mode 100644 index 000000000000..1db3faffd10a --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp @@ -0,0 +1,88 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteDebuggerDummyUnwinder.hpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Code to unwind past a debugger's dummy frame inserted when it does an +// inferior function call. +// In this case we'll need to get the saved register context from the debugger - +// it may be in the debugger's local memory or it may be saved in a nonstandard +// location in the inferior process' memory. + +#ifndef __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__ +#define __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__ + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include "libunwind.h" +#include "Registers.hpp" +#include "AddressSpace.hpp" +#include "RemoteRegisterMap.hpp" +#include "RemoteProcInfo.hpp" + +namespace lldb_private +{ + +template +int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_x86_64& registers, + RemoteProcInfo *procinfo, uint64_t ip, + uint64_t sp, void* arg) +{ + Registers_x86_64 newRegisters(registers); + RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap(); + unw_word_t regv; + for (int i = UNW_X86_64_RAX; i <= UNW_X86_64_R15; i++) { + int driver_regnum; + if (!rmap->unwind_regno_to_caller_regno (i, driver_regnum)) + continue; + if (addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, driver_regnum, ®v, 0, arg)) + newRegisters.setRegister(i, regv); + } + if (!addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, rmap->caller_regno_for_ip(), ®v, 0, arg)) + return UNW_EUNSPEC; + newRegisters.setIP (regv); + registers = newRegisters; + return UNW_STEP_SUCCESS; +} + +template +int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_x86& registers, + RemoteProcInfo *procinfo, uint64_t ip, + uint64_t sp, void* arg) +{ + Registers_x86 newRegisters(registers); + RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap(); + unw_word_t regv; + for (int i = UNW_X86_EAX; i <= UNW_X86_EDI; i++) { + int driver_regnum; + if (!rmap->unwind_regno_to_caller_regno (i, driver_regnum)) + continue; + if (addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, driver_regnum, ®v, 0, arg)) + newRegisters.setRegister(i, regv); + } + if (!addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, rmap->caller_regno_for_ip(), ®v, 0, arg)) + return UNW_EUNSPEC; + newRegisters.setIP (regv); + registers = newRegisters; + return UNW_STEP_SUCCESS; +} + +template +int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_ppc& registers, + uint64_t ip, uint64_t sp) +{ + ABORT ("stepping out of a debugger dummy frame not supported on ppc"); + return UNW_EUNSPEC; +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING + +#endif // __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__ + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp new file mode 100644 index 000000000000..3640dc6195c9 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp @@ -0,0 +1,977 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteProcInfo.hpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file defines the primary object created when unw_create_addr_space() +// is called. This object tracks the list of known images in memory +// (dylibs, bundles, etc), it maintains a link to a RemoteRegisterMap for this +// architecture, it caches the remote process memory in a local store and all +// read/writes are filtered through its accessors which will use the memory +// caches. It maintains a logging level set by the driver program and puts +// timing/debug messages out on a FILE* provided to it. + +// RemoteProcInfo is not specific to any particular unwind so it does not +// maintain an "arg" argument (an opaque pointer that the driver program uses +// to track the process/thread being unwound). + +#ifndef __REMOTE_PROC_INFO_HPP__ +#define __REMOTE_PROC_INFO_HPP__ + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "FileAbstraction.hpp" +#include "libunwind.h" +#include "InternalMacros.h" +#include "dwarf2.h" +#include "RemoteUnwindProfile.h" +#include "Registers.hpp" +#include "RemoteRegisterMap.hpp" + +namespace lldb_private +{ +class RemoteProcInfo; + +/// +/// unw_addr_space_remote is the concrete instance that a unw_addr_space_t points to when examining +/// a remote process. +/// +struct unw_addr_space_remote +{ + enum unw_as_type type; // should always be UNW_REMOTE + RemoteProcInfo* ras; +}; + +class RemoteMemoryBlob +{ +public: + typedef void (*free_callback_with_arg)(void *, void*); + typedef void (*free_callback)(void *); + + /* This object is constructed with a callback to free the memory; + that callback takes a pointer to the memory region and optionally + takes an additional argument -- the "void* arg" passed around for + remote unwinds, in case the driver program allocated this e.g. with + mach_vm_read, and needs the token to vm_deallocate it. */ + + RemoteMemoryBlob (uint8_t *buf, free_callback_with_arg to_free, + uint64_t startaddr, uint64_t len, uint64_t mh, void *arg) : + fBuf(buf), fToFreeWithArg(to_free), fToFree(NULL), + fStartAddr(startaddr), fLen(len), fMachHeader(mh), + fArg(arg) { } + RemoteMemoryBlob (uint8_t *buf, free_callback to_free, uint64_t startaddr, + uint64_t len, uint64_t mh, void *arg) : + fBuf(buf), fToFree(to_free), fToFreeWithArg(NULL), + fStartAddr(startaddr), fLen(len), fMachHeader(mh), + fArg(NULL) { } + + // the following is to create a dummy RMB object for lower_bound's use in + // searching. + RemoteMemoryBlob (uint64_t startaddr) : fStartAddr(startaddr), fToFree(NULL), + fBuf(NULL), fToFreeWithArg(NULL), fArg(NULL), fMachHeader(-1), + fLen(0) { } + ~RemoteMemoryBlob () { + if (fToFreeWithArg) + fToFreeWithArg(fBuf, fArg); + else if (fToFree) + fToFree(fBuf); + } + bool contains_addr (uint64_t addr) { + if (fStartAddr <= addr && addr < fStartAddr + fLen) + return true; + else + return false; + } + uint8_t *get_blob_range (uint64_t remote_process_addr, int len) { + if (this->contains_addr (remote_process_addr) == false) + return NULL; + if (this->contains_addr (remote_process_addr + len) == false) + return NULL; + return fBuf + (remote_process_addr - fStartAddr); + } + uint64_t getMh () const { return fMachHeader; } + uint64_t getStartAddr() const { return fStartAddr; } + uint64_t getLength() const { return fLen; } +private: + uint8_t *fBuf; + free_callback fToFree; + free_callback_with_arg fToFreeWithArg; + uint64_t fStartAddr; + uint64_t fLen; + uint64_t fMachHeader; + void *fArg; +}; + +inline bool operator<(const RemoteMemoryBlob &b1, const RemoteMemoryBlob &b2) { + if (b1.getStartAddr() < b2.getStartAddr()) + return true; + else + return false; +} + +// One of these for each image in memory (executable, dylib, bundle, etc) + +struct RemoteImageEntry +{ + RemoteImageEntry () : mach_header(0), text_start(0), text_end(0), eh_frame_start(0), eh_frame_len(0), compact_unwind_info_start(0), compact_unwind_info_len(0) { } + ~RemoteImageEntry () { + std::map::iterator i; + for (i = profiles.begin(); i != profiles.end(); ++i) + delete i->second; + } + uint64_t mach_header; + uint64_t text_start; + uint64_t text_end; + uint64_t eh_frame_start; + uint64_t eh_frame_len; + uint64_t compact_unwind_info_start; + uint64_t compact_unwind_info_len; + + // unwind profiles created for thsi binary image so far, + // key is the start address of the profile. + std::map profiles; + + // a list of function address bounds for this binary image - + // end addresses should be accurate and not inferred from potentially + // incomplete start-address data (e.g. nlist records). + std::vector func_bounds; +}; + +class RemoteImages +{ +public: + RemoteImages (unw_targettype_t targarch) : fTargetArch(targarch) { } + ~RemoteImages (); + void removeAllImageProfiles(); + void removeOneImageProfiles(uint64_t mh); + RemoteImageEntry *remoteEntryForTextAddr (uint64_t pc); + bool addFuncBounds (uint64_t mh, std::vector &startAddrs); + bool haveFuncBounds (uint64_t mh); + bool findFuncBounds (uint32_t pc, uint32_t &startAddr, uint32_t &endAddr); + bool findFuncBounds (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr); + void addImage (uint64_t mh, uint64_t text_start, uint64_t text_end, uint64_t eh_frame, uint64_t eh_frame_len, uint64_t compact_unwind_start, uint64_t compact_unwind_len); + bool addProfile (RemoteProcInfo* procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg); + RemoteUnwindProfile* findProfileByTextAddr (uint64_t pc); + void addMemBlob (RemoteMemoryBlob *blob); + uint8_t *getMemBlobMemory (uint64_t addr, int len); +private: + RemoteImages(); + std::map fImages; + std::vector fMemBlobs; + unw_targettype_t fTargetArch; +}; + +RemoteImages::~RemoteImages () { + std::map >::iterator i; + std::vector::iterator j; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) { + delete *j; + } + fMemBlobs.erase(fMemBlobs.begin(), fMemBlobs.end()); +} + +void RemoteImages::removeAllImageProfiles() { + fImages.erase(fImages.begin(), fImages.end()); + std::vector::iterator j; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) + delete *j; + fMemBlobs.erase(fMemBlobs.begin(), fMemBlobs.end()); +} + +void RemoteImages::removeOneImageProfiles(uint64_t mh) { + std::map::iterator i; + i = fImages.find(mh); + if (i != fImages.end()) + fImages.erase(i); + + std::vector::iterator j; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) { + if ((*j)->getMh() == mh) { + delete *j; + break; + } + } + if (j != fMemBlobs.end()) + fMemBlobs.erase(j); +} + +RemoteImageEntry *RemoteImages::remoteEntryForTextAddr (uint64_t pc) { + std::map::iterator i = fImages.lower_bound (pc); + if (i == fImages.begin() && i == fImages.end()) + return NULL; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != pc) + --i; + } + if (i->second.text_start <= pc && i->second.text_end > pc) + { + return &(i->second); + } + else + { + return NULL; + } +} + +bool RemoteImages::addFuncBounds (uint64_t mh, std::vector &startAddrs) { + RemoteImageEntry *img = NULL; + std::map::iterator i = fImages.find (mh); + if (i == fImages.end()) + return false; + img = &i->second; + img->func_bounds = startAddrs; + std::sort(img->func_bounds.begin(), img->func_bounds.end()); + return true; +} + +bool RemoteImages::haveFuncBounds (uint64_t mh) { + RemoteImageEntry *img = NULL; + std::map::iterator i = fImages.find (mh); + if (i == fImages.end()) + return false; + img = &i->second; + if (img->func_bounds.size() > 0) + return true; + return false; +} + +bool RemoteImages::findFuncBounds (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr) { + RemoteImageEntry *img = NULL; + startAddr = endAddr = 0; + std::map::iterator i = fImages.lower_bound (pc); + if (i == fImages.begin() && i == fImages.end()) + return false; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != pc) + --i; + } + if (i->second.text_start <= pc && i->second.text_end > pc) + { + img = &i->second; + } + else + return false; + std::vector::iterator j; + j = std::lower_bound(img->func_bounds.begin(), img->func_bounds.end(), FuncBounds (pc, pc)); + if (j == img->func_bounds.begin() && j == img->func_bounds.end()) + return false; + if (j == img->func_bounds.end()) { + --j; + } else { + if (j != img->func_bounds.begin() && j->fStart != pc) + --j; + } + if (j->fStart <= pc && j->fEnd > pc) { + startAddr = j->fStart; + endAddr = j->fEnd; + return true; + } + return false; +} + +// Add 32-bit version of findFuncBounds so we can avoid templatizing all of these functions +// just to handle 64 and 32 bit unwinds. + +bool RemoteImages::findFuncBounds (uint32_t pc, uint32_t &startAddr, uint32_t &endAddr) { + uint64_t big_startAddr = startAddr; + uint64_t big_endAddr = endAddr; + bool ret; + ret = findFuncBounds (pc, big_startAddr, big_endAddr); + startAddr = (uint32_t) big_startAddr & 0xffffffff; + endAddr = (uint32_t) big_endAddr & 0xffffffff; + return ret; +} + +// Make sure we don't cache the same memory range more than once +// I'm not checking the length of the blobs to check for overlap - +// as this is used today, the only duplication will be with the same +// start address. + +void RemoteImages::addMemBlob (RemoteMemoryBlob *blob) { + std::vector::iterator i; + for (i = fMemBlobs.begin(); i != fMemBlobs.end(); ++i) { + if (blob->getStartAddr() == (*i)->getStartAddr()) + return; + } + fMemBlobs.push_back(blob); + std::sort(fMemBlobs.begin(), fMemBlobs.end()); +} + +uint8_t *RemoteImages::getMemBlobMemory (uint64_t addr, int len) { + uint8_t *res = NULL; + std::vector::iterator j; + RemoteMemoryBlob *searchobj = new RemoteMemoryBlob(addr); + j = std::lower_bound (fMemBlobs.begin(), fMemBlobs.end(), searchobj); + delete searchobj; + if (j == fMemBlobs.end() && j == fMemBlobs.begin()) + return NULL; + if (j == fMemBlobs.end()) { + --j; + } else { + if (j != fMemBlobs.begin() && (*j)->getStartAddr() != addr) + --j; + } + res = (*j)->get_blob_range (addr, len); + if (res != NULL) + return res; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) { + res = (*j)->get_blob_range (addr, len); + if (res != NULL) + break; + } + return res; +} + +void RemoteImages::addImage (uint64_t mh, uint64_t text_start, + uint64_t text_end, uint64_t eh_frame, + uint64_t eh_frame_len, + uint64_t compact_unwind_start, + uint64_t compact_unwind_len) { + struct RemoteImageEntry img; + img.mach_header = mh; + img.text_start = text_start; + img.text_end = text_end; + img.eh_frame_start = eh_frame; + img.eh_frame_len = eh_frame_len; + img.compact_unwind_info_start = compact_unwind_start; + img.compact_unwind_info_len = compact_unwind_len; + fImages[mh] = img; +} + +// The binary image for this start/end address must already be present +bool RemoteImages::addProfile (RemoteProcInfo* procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg) { + RemoteImageEntry *img = NULL; + std::map::iterator i = fImages.lower_bound (start); + if (i == fImages.begin() && i == fImages.end()) + return false; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != start) { + --i; + } + } + if (i->second.text_start <= start && i->second.text_end > start) + { + img = &i->second; + } + else + return false; + RemoteUnwindProfile* profile = new RemoteUnwindProfile; + if (AssemblyParse (procinfo, acc, as, start, end, *profile, arg)) { + img->profiles[start] = profile; + return true; + } + return false; +} + +RemoteUnwindProfile* RemoteImages::findProfileByTextAddr (uint64_t pc) { + RemoteImageEntry *img = NULL; + std::map::iterator i = fImages.lower_bound (pc); + if (i == fImages.begin() && i == fImages.end()) + return NULL; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != pc) + --i; + } + if (i->second.text_start <= pc && i->second.text_end > pc) + { + img = &i->second; + } + else + return false; + std::map::iterator j; + j = img->profiles.lower_bound (pc); + if (j == img->profiles.begin() && j == img->profiles.end()) + return NULL; + if (j == img->profiles.end()) { + --j; + } else { + if (j != img->profiles.begin() && j->first != pc) + --j; + } + if (j->second->fStart <= pc && j->second->fEnd > pc) + { + return j->second; + } + return NULL; +} + +/// +/// RemoteProcInfo is used as a template parameter to UnwindCursor when +/// unwinding a thread that has a custom set of accessors. It calls the +/// custom accessors for all data. +/// +class RemoteProcInfo +{ +public: + +// libunwind documentation specifies that unw_create_addr_space defaults to +// UNW_CACHE_NONE but that's going to work very poorly for us so we're +// defaulting to UNW_CACHE_GLOBAL. + + RemoteProcInfo(unw_accessors_t* accessors, unw_targettype_t targarch) : + fAccessors(*accessors), fCachingPolicy(UNW_CACHE_GLOBAL), + fTargetArch(targarch), fImages(targarch), fLogging(NULL), + fLogLevel(UNW_LOG_LEVEL_NONE) + { + fWrapper.type = UNW_REMOTE; + fWrapper.ras = this; + fRemoteRegisterMap = new RemoteRegisterMap(accessors, targarch); + if (fTargetArch == UNW_TARGET_X86_64 || fTargetArch == UNW_TARGET_I386 + || fTargetArch == UNW_TARGET_ARM) + fLittleEndian = true; + else + fLittleEndian = false; + } + + ~RemoteProcInfo () { + delete fRemoteRegisterMap; + } + + bool haveProfile (uint64_t pc) { + if (fImages.findProfileByTextAddr (pc)) + return true; + else + return false; + } + + // returns NULL if profile does not yet exist. + RemoteUnwindProfile* findProfile (uint64_t pc) { + return fImages.findProfileByTextAddr (pc); + } + + // returns NULL if the binary image is not yet added. + bool addProfile (unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg) { + if (fImages.addProfile (this, acc, as, start, end, arg)) + return true; + else + return false; + } + + bool haveImageEntry (uint64_t pc, void *arg); + + bool getImageAddresses (uint64_t pc, uint64_t &mh, uint64_t &text_start, uint64_t &text_end, + uint64_t &eh_frame_start, uint64_t &eh_frame_len, uint64_t &compact_unwind_start, + void *arg); + bool getImageAddresses (uint64_t pc, uint32_t &mh, uint32_t &text_start, uint32_t &text_end, + uint32_t &eh_frame_start, uint32_t &eh_frame_len, uint32_t &compact_unwind_start, + void *arg); + + bool addFuncBounds (uint64_t mh, std::vector &startAddrs) { return fImages.addFuncBounds (mh, startAddrs); } + bool haveFuncBounds (uint64_t mh) { return fImages.haveFuncBounds (mh); } + bool findStartAddr (uint64_t pc, uint32_t &startAddr, uint32_t &endAddr) { return fImages.findFuncBounds (pc, startAddr, endAddr); } + bool findStartAddr (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr) { return fImages.findFuncBounds (pc, startAddr, endAddr); } + uint8_t *getMemBlobMemory (uint64_t addr, int len) { return fImages.getMemBlobMemory (addr, len); } + + + // Functions to pull memory from the target into the debugger. + + int getBytes(uint64_t addr, uint64_t extent, uint8_t* buf, void* arg) + { + int err = readRaw(addr, extent, buf, arg); + + if(err) + return 0; + + return 1; + } + +#define DECLARE_INT_ACCESSOR(bits) \ + uint##bits##_t get##bits(uint64_t addr, void* arg) \ + { \ + uint##bits##_t ret; \ + int err = readRaw(addr, (unw_word_t)(bits / 8), (uint8_t*)&ret, arg); \ + \ + if(err) \ + ABORT("Invalid memory access in the target"); \ + \ + return ret; \ + } + DECLARE_INT_ACCESSOR(8) + DECLARE_INT_ACCESSOR(16) + DECLARE_INT_ACCESSOR(32) + DECLARE_INT_ACCESSOR(64) +#undef DECLARE_INT_ACCESSOR + +// 'err' is set to 0 if there were no errors reading this +// memory. Non-zero values indicate that the memory was not +// read successfully. This method should be preferred over the +// method above which asserts on failure. + +#define DECLARE_INT_ACCESSOR_ERR(bits) \ + uint##bits##_t get##bits(uint64_t addr, int &err, void* arg) \ + { \ + uint##bits##_t ret; \ + err = readRaw(addr, (unw_word_t)(bits / 8), (uint8_t*)&ret, arg); \ + \ + return ret; \ + } + DECLARE_INT_ACCESSOR_ERR(8) + DECLARE_INT_ACCESSOR_ERR(16) + DECLARE_INT_ACCESSOR_ERR(32) + DECLARE_INT_ACCESSOR_ERR(64) +#undef DECLARE_INT_ACCESSOR_ERR + + double getDouble(uint64_t addr, void* arg) + { + double ret; + int err = readRaw(addr, (unw_word_t)(sizeof(ret) / 8), (uint8_t*)&ret, arg); + if(err) + ABORT("Invalid memory access in the target"); + return ret; + } + + v128 getVector(uint64_t addr, void* arg) + { + v128 ret; + int err = readRaw(addr, (unw_word_t)(sizeof(ret) / 8), (uint8_t*)&ret, arg); + if(err) + ABORT("Invalid memory access in the target"); + return ret; + } + + // Pull an unsigned LEB128 from the target into the debugger as a uint64_t. + uint64_t getULEB128(uint64_t& addr, uint64_t end, void* arg) + { + uint64_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + addr = lAddr; + return ret; + } + + // Pull an unsigned LEB128 from the target into the debugger as a uint64_t. + uint64_t getULEB128(uint32_t& addr, uint32_t end, void* arg) + { + uint32_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + addr = lAddr; + return ret; + } + + + // Pull a signed LEB128 from the target into the debugger as a uint64_t. + int64_t getSLEB128(uint64_t& addr, uint64_t end, void* arg) + { + uint64_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + // Sign-extend + if((shift < (sizeof(int64_t) * 8)) && (byte & 0x40)) + ret |= -(1 << shift); + addr = lAddr; + return ret; + } + + // Pull a signed LEB128 from the target into the debugger as a uint64_t. + int64_t getSLEB128(uint32_t& addr, uint32_t end, void* arg) + { + uint32_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + // Sign-extend + if((shift < (sizeof(int64_t) * 8)) && (byte & 0x40)) + ret |= -(1 << shift); + addr = lAddr; + return ret; + } + + + uint64_t getP (uint64_t addr, void *arg) { + switch (fTargetArch) { + case UNW_TARGET_X86_64: + return get64(addr, arg); + break; + case UNW_TARGET_I386: + return get32(addr, arg); + break; + } + ABORT("Unknown target architecture."); + return 0; + } + + uint64_t getP (uint64_t addr, int& err, void *arg) { + switch (fTargetArch) { + case UNW_TARGET_X86_64: + return get64(addr, err, arg); + break; + case UNW_TARGET_I386: + return get32(addr, err, arg); + break; + } + ABORT("Unknown target architecture."); + return 0; + } + + bool findFunctionName(uint64_t addr, char *buf, size_t bufLen, unw_word_t *offset, void* arg); + bool findFunctionBounds(uint64_t addr, uint64_t& low, uint64_t& high, void* arg); + int setCachingPolicy(unw_caching_policy_t policy); + + void setLoggingLevel(FILE *f, unw_log_level_t level); + void logInfo(const char *fmt, ...); + void logAPI(const char *fmt, ...); + void logVerbose(const char *fmt, ...); + void logDebug(const char *fmt, ...); + struct timeval *timestamp_start (); + void timestamp_stop (struct timeval *tstart, const char *fmt, ...); + + void flushAllCaches() { fImages.removeAllImageProfiles(); } + void flushCacheByMachHeader(uint64_t mh) { fImages.removeOneImageProfiles(mh); } + unw_targettype_t getTargetArch() { return fTargetArch; } + unw_accessors_t* getAccessors () { return &fAccessors; } + RemoteRegisterMap* getRegisterMap() { return fRemoteRegisterMap; } + unw_addr_space_t wrap () { return (unw_addr_space_t) &fWrapper; } + bool remoteIsLittleEndian () { return fLittleEndian; } + unw_log_level_t getDebugLoggingLevel() { return fLogLevel; } + void addMemBlob (RemoteMemoryBlob *blob) { fImages.addMemBlob(blob); } + unw_caching_policy_t getCachingPolicy() { return fCachingPolicy; } + +private: + int readRaw(uint64_t addr, uint64_t extent, uint8_t *valp, void* arg) + { + uint8_t *t = this->getMemBlobMemory (addr, extent); + if (t) { + memcpy (valp, t, extent); + return 0; + } + return fAccessors.access_raw((unw_addr_space_t)this, addr, extent, valp, 0, arg); + } + + struct unw_addr_space_remote fWrapper; + unw_accessors_t fAccessors; + unw_caching_policy_t fCachingPolicy; + unw_targettype_t fTargetArch; + unw_addr_space_t fAddrSpace; + RemoteImages fImages; + RemoteRegisterMap *fRemoteRegisterMap; + FILE *fLogging; + unw_log_level_t fLogLevel; + bool fLittleEndian; +}; + +// Find an image containing the given pc, returns false if absent and +// we can't add it via the accessors. +bool RemoteProcInfo::haveImageEntry (uint64_t pc, void *arg) { + if (fImages.remoteEntryForTextAddr (pc) == NULL) { + unw_word_t mh, text_start, text_end, eh_frame, eh_frame_len, compact_unwind, compact_unwind_len; + if (fAccessors.find_image_info (wrap(), pc, &mh, &text_start, + &text_end, &eh_frame, &eh_frame_len, &compact_unwind, &compact_unwind_len, arg) == UNW_ESUCCESS) { + fImages.addImage (mh, text_start, text_end, eh_frame, eh_frame_len, compact_unwind, compact_unwind_len); + if (fCachingPolicy != UNW_CACHE_NONE) { + if (compact_unwind_len != 0) { + logVerbose ("Creating RemoteMemoryBlob of compact unwind info image at mh 0x%llx, %lld bytes", mh, (uint64_t) compact_unwind_len); + uint8_t *buf = (uint8_t*) malloc (compact_unwind_len); + if (this->getBytes (compact_unwind, compact_unwind_len, buf, arg)) { + RemoteMemoryBlob *b = new RemoteMemoryBlob(buf, free, compact_unwind, compact_unwind_len, mh, NULL); + fImages.addMemBlob (b); + } + } else if (eh_frame_len != 0) { + logVerbose ("Creating RemoteMemoryBlob of eh_frame for image at mh 0x%llx, %lld bytes", mh, (uint64_t) compact_unwind_len); + uint8_t *buf = (uint8_t*) malloc (eh_frame_len); + if (this->getBytes (eh_frame, eh_frame_len, buf, arg)) { + RemoteMemoryBlob *b = new RemoteMemoryBlob(buf, free, eh_frame, eh_frame_len, mh, NULL); + fImages.addMemBlob (b); + } + } + } + } else { + return false; /// find_image_info failed + } + } else { + return true; + } + return true; +} + +bool RemoteProcInfo::getImageAddresses (uint64_t pc, uint64_t &mh, uint64_t &text_start, uint64_t &text_end, + uint64_t &eh_frame_start, uint64_t &eh_frame_len, uint64_t &compact_unwind_start, + void *arg) { + // Make sure we have this RemoteImageEntry already - fetch it now if needed. + if (haveImageEntry (pc, arg) == false) { + return false; + } + RemoteImageEntry *r = fImages.remoteEntryForTextAddr (pc); + if (r) { + mh = r->mach_header; + text_start = r->text_start; + text_end = r->text_end; + eh_frame_start = r->eh_frame_start; + eh_frame_len = r->eh_frame_len; + compact_unwind_start = r->compact_unwind_info_start; + return true; + } + return false; +} + + +bool RemoteProcInfo::findFunctionName(uint64_t addr, char *buf, size_t bufLen, unw_word_t *offset, void* arg) +{ + if(fAccessors.get_proc_name(wrap(), addr, buf, bufLen, offset, arg) == UNW_ESUCCESS) + return true; + else + return false; +} + +bool RemoteProcInfo::findFunctionBounds(uint64_t addr, uint64_t& low, uint64_t& high, void* arg) +{ + if (fAccessors.get_proc_bounds(wrap(), addr, &low, &high, arg) == UNW_ESUCCESS + && high != 0) + return true; + else + return false; +} + +int RemoteProcInfo::setCachingPolicy(unw_caching_policy_t policy) +{ + if(policy == UNW_CACHE_NONE && fCachingPolicy != UNW_CACHE_NONE) + { + flushAllCaches(); + } + + if(!(policy == UNW_CACHE_NONE || policy == UNW_CACHE_GLOBAL || policy == UNW_CACHE_PER_THREAD)) + return UNW_EINVAL; + + fCachingPolicy = policy; + + return UNW_ESUCCESS; +} + +void RemoteProcInfo::setLoggingLevel(FILE *f, unw_log_level_t level) +{ + fLogLevel = level; + fLogging = f; +} + +void RemoteProcInfo::logInfo(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_INFO) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +void RemoteProcInfo::logAPI(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_API) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +void RemoteProcInfo::logVerbose(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_VERBOSE) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +void RemoteProcInfo::logDebug(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_DEBUG) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +struct timeval *RemoteProcInfo::timestamp_start () +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return NULL; + if (fLogLevel & UNW_LOG_LEVEL_TIMINGS) { + struct timeval *t = (struct timeval *) malloc (sizeof (struct timeval)); + if (gettimeofday (t, NULL) != 0) { + free (t); + return NULL; + } + return t; + } + return NULL; +} + +void RemoteProcInfo::timestamp_stop (struct timeval *tstart, const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE || tstart == NULL) + return; + if (fLogLevel & UNW_LOG_LEVEL_TIMINGS) { + struct timeval tend; + if (gettimeofday (&tend, NULL) != 0) { + free (tstart); + return; + } + struct timeval result; + timersub (&tend, tstart, &result); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + printf (" duration %0.5fs\n", (double) ((result.tv_sec * 1000000) + result.tv_usec) / 1000000.0); + va_end (ap); + free (tstart); + } +} + + +// Initialize the register context at the start of a remote unwind. + +void getRemoteContext (RemoteProcInfo* procinfo, Registers_x86_64& r, void *arg) { + unw_accessors_t* accessors = procinfo->getAccessors(); + unw_addr_space_t addrSpace = procinfo->wrap(); + RemoteRegisterMap* regmap = procinfo->getRegisterMap(); + uint64_t rv; + + // now that we have a selected process/thread, ask about the valid registers. + regmap->scan_caller_regs (addrSpace, arg); + +#define FILLREG(reg) {int caller_reg; regmap->unwind_regno_to_caller_regno ((reg), caller_reg); accessors->access_reg (addrSpace, caller_reg, &rv, 0, arg); r.setRegister ((reg), rv);} + FILLREG (UNW_X86_64_RAX); + FILLREG (UNW_X86_64_RDX); + FILLREG (UNW_X86_64_RCX); + FILLREG (UNW_X86_64_RBX); + FILLREG (UNW_X86_64_RSI); + FILLREG (UNW_X86_64_RDI); + FILLREG (UNW_X86_64_RBP); + FILLREG (UNW_X86_64_RSP); + FILLREG (UNW_X86_64_R8); + FILLREG (UNW_X86_64_R9); + FILLREG (UNW_X86_64_R10); + FILLREG (UNW_X86_64_R11); + FILLREG (UNW_X86_64_R12); + FILLREG (UNW_X86_64_R13); + FILLREG (UNW_X86_64_R14); + FILLREG (UNW_X86_64_R15); + FILLREG (UNW_REG_IP); +#undef FILLREG +} + +void getRemoteContext (RemoteProcInfo* procinfo, Registers_x86& r, void *arg) { + unw_accessors_t* accessors = procinfo->getAccessors(); + unw_addr_space_t addrSpace = procinfo->wrap(); + RemoteRegisterMap* regmap = procinfo->getRegisterMap(); + uint64_t rv; + + // now that we have a selected process/thread, ask about the valid registers. + regmap->scan_caller_regs (addrSpace, arg); + +#define FILLREG(reg) {int caller_reg; regmap->unwind_regno_to_caller_regno ((reg), caller_reg); accessors->access_reg (addrSpace, caller_reg, &rv, 0, arg); r.setRegister ((reg), rv);} + FILLREG (UNW_X86_EAX); + FILLREG (UNW_X86_ECX); + FILLREG (UNW_X86_EDX); + FILLREG (UNW_X86_EBX); + FILLREG (UNW_X86_EBP); + FILLREG (UNW_X86_ESP); + FILLREG (UNW_X86_ESI); + FILLREG (UNW_X86_EDI); + FILLREG (UNW_REG_IP); +#undef FILLREG +} + + +void getRemoteContext (RemoteProcInfo* procinfo, Registers_ppc& r, void *arg) { + ABORT("ppc get remote context not implemented."); +} + +}; // namespace lldb_private + + + +#endif // SUPPORT_REMOTE_UNWINDING +#endif // __REMOTE_PROC_INFO_HPP__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp new file mode 100644 index 000000000000..19caae9a3f48 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp @@ -0,0 +1,405 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteRegisterMap.hpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Provide conversions between reigster names, the libunwind internal enums, +// and the register numbers the program calling libunwind are using. + +#ifndef __REMOTE_REGISTER_MAP_HPP__ +#define __REMOTE_REGISTER_MAP_HPP__ + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include "libunwind.h" +#include + +namespace lldb_private +{ +class RemoteRegisterMap { +public: + RemoteRegisterMap (unw_accessors_t *accessors, unw_targettype_t target); + ~RemoteRegisterMap (); + void initialize_x86_64 (); + void initialize_i386 (); + bool name_to_caller_regno (const char *name, int& callerr); + bool name_to_unwind_regno (const char *name, int& unwindr); + bool unwind_regno_to_caller_regno (int unwindr, int& callerr); + bool nonvolatile_reg_p (int unwind_regno); + bool argument_regnum_p (int unwind_regno); + const char *ip_register_name(); + const char *sp_register_name(); + int caller_regno_for_ip (); + int caller_regno_for_sp (); + int unwind_regno_for_frame_pointer (); + int unwind_regno_for_stack_pointer (); + int wordsize () { return fWordSize; } + void scan_caller_regs (unw_addr_space_t as, void *arg); + + bool unwind_regno_to_machine_regno (int unwindr, int& machiner); + bool machine_regno_to_unwind_regno (int machr, int& unwindr); + bool caller_regno_to_unwind_regno (int callerr, int& unwindr); + const char* unwind_regno_to_name (int unwindr); + int byte_size_for_regtype (unw_regtype_t type); + +private: + + // A structure that collects everything we need to know about a + // given register in one place. + struct reg { + int unwind_regno; // What libunwind-remote uses internally + int caller_regno; // What the libunwind-remote driver program uses + int eh_frame_regno; // What the eh_frame section uses + int machine_regno; // What the actual bits/bytes are in instructions + char *name; + unw_regtype_t type; + reg () : unwind_regno(-1), machine_regno(-1), caller_regno(-1), + eh_frame_regno(-1), name(NULL), type(UNW_NOT_A_REG) { } + }; + + unw_accessors_t fAccessors; + unw_targettype_t fTarget; + std::vector fRegMap; + int fWordSize; +}; + +void RemoteRegisterMap::initialize_x86_64 () { +#define DEFREG(ureg, ehno, machno, regn) {RemoteRegisterMap::reg r; r.unwind_regno = ureg; r.name = regn; r.eh_frame_regno = ehno; r.machine_regno = machno; r.type = UNW_INTEGER_REG; fRegMap.push_back(r); } + DEFREG (UNW_X86_64_RAX, 0, 0, strdup ("rax")); + DEFREG (UNW_X86_64_RDX, 1, 2, strdup ("rdx")); + DEFREG (UNW_X86_64_RCX, 2, 1, strdup ("rcx")); + DEFREG (UNW_X86_64_RBX, 3, 3, strdup ("rbx")); + DEFREG (UNW_X86_64_RSI, 4, 6, strdup ("rsi")); + DEFREG (UNW_X86_64_RDI, 5, 7, strdup ("rdi")); + DEFREG (UNW_X86_64_RBP, 6, 5, strdup ("rbp")); + DEFREG (UNW_X86_64_RSP, 7, 4, strdup ("rsp")); + DEFREG (UNW_X86_64_R8, 8, 8, strdup ("r8")); + DEFREG (UNW_X86_64_R9, 9, 9, strdup ("r9")); + DEFREG (UNW_X86_64_R10, 10, 10, strdup ("r10")); + DEFREG (UNW_X86_64_R11, 11, 11, strdup ("r11")); + DEFREG (UNW_X86_64_R12, 12, 12, strdup ("r12")); + DEFREG (UNW_X86_64_R13, 13, 13, strdup ("r13")); + DEFREG (UNW_X86_64_R14, 14, 14, strdup ("r14")); + DEFREG (UNW_X86_64_R15, 15, 15, strdup ("r15")); +#undef DEFREG + RemoteRegisterMap::reg r; + r.name = strdup ("rip"); + r.type = UNW_INTEGER_REG; + r.eh_frame_regno = 16; + fRegMap.push_back(r); +} + +void RemoteRegisterMap::initialize_i386 () { +#define DEFREG(ureg, ehno, machno, regn) {RemoteRegisterMap::reg r; r.unwind_regno = ureg; r.name = regn; r.eh_frame_regno = ehno; r.machine_regno = machno; r.type = UNW_INTEGER_REG; fRegMap.push_back(r); } + DEFREG (UNW_X86_EAX, 0, 0, strdup ("eax")); + DEFREG (UNW_X86_ECX, 1, 1, strdup ("ecx")); + DEFREG (UNW_X86_EDX, 2, 2, strdup ("edx")); + DEFREG (UNW_X86_EBX, 3, 3, strdup ("ebx")); + // i386 EH frame info has the next two swapped, + // v. gcc/config/i386/darwin.h:DWARF2_FRAME_REG_OUT. + DEFREG (UNW_X86_EBP, 4, 5, strdup ("ebp")); + DEFREG (UNW_X86_ESP, 5, 4, strdup ("esp")); + DEFREG (UNW_X86_ESI, 6, 6, strdup ("esi")); + DEFREG (UNW_X86_EDI, 7, 7, strdup ("edi")); +#undef DEFREG + RemoteRegisterMap::reg r; + r.name = strdup ("eip"); + r.type = UNW_INTEGER_REG; + r.eh_frame_regno = 8; + fRegMap.push_back(r); +} + + +RemoteRegisterMap::RemoteRegisterMap (unw_accessors_t *accessors, unw_targettype_t target) { + fAccessors = *accessors; + fTarget = target; + switch (target) { + case UNW_TARGET_X86_64: + this->initialize_x86_64(); + fWordSize = 8; + break; + case UNW_TARGET_I386: + this->initialize_i386(); + fWordSize = 4; + break; + default: + ABORT("RemoteRegisterMap called with unknown target"); + } +} + +RemoteRegisterMap::~RemoteRegisterMap () { + std::vector::iterator j; + for (j = fRegMap.begin(); j != fRegMap.end(); ++j) + free (j->name); +} + +bool RemoteRegisterMap::name_to_caller_regno (const char *name, int& callerr) { + if (name == NULL) + return false; + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (strcasecmp (j->name, name) == 0) { + callerr = j->caller_regno; + return true; + } + return false; +} + +bool RemoteRegisterMap::unwind_regno_to_caller_regno (int unwindr, int& callerr) { + if (unwindr == UNW_REG_IP) { + callerr = this->caller_regno_for_ip (); + return true; + } + if (unwindr == UNW_REG_SP) { + callerr = this->caller_regno_for_sp (); + return true; + } + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->unwind_regno == unwindr && j->caller_regno != -1) { + callerr = j->caller_regno; + return true; + } + return false; +} + +bool RemoteRegisterMap::nonvolatile_reg_p (int unwind_regno) { + if (fTarget == UNW_TARGET_X86_64) { + switch (unwind_regno) { + case UNW_X86_64_RBX: + case UNW_X86_64_RSP: + case UNW_X86_64_RBP: // not actually a nonvolatile but often treated as such by convention + case UNW_X86_64_R12: + case UNW_X86_64_R13: + case UNW_X86_64_R14: + case UNW_X86_64_R15: + case UNW_REG_IP: + case UNW_REG_SP: + return true; + break; + default: + return false; + } + } + if (fTarget == UNW_TARGET_I386) { + switch (unwind_regno) { + case UNW_X86_EBX: + case UNW_X86_EBP: // not actually a nonvolatile but often treated as such by convention + case UNW_X86_ESI: + case UNW_X86_EDI: + case UNW_X86_ESP: + case UNW_REG_IP: + case UNW_REG_SP: + return true; + break; + default: + return false; + } + } + return false; +} + + +bool RemoteRegisterMap::argument_regnum_p (int unwind_regno) { + if (fTarget == UNW_TARGET_X86_64) { + switch (unwind_regno) { + case UNW_X86_64_RDI: /* arg 1 */ + case UNW_X86_64_RSI: /* arg 2 */ + case UNW_X86_64_RDX: /* arg 3 */ + case UNW_X86_64_RCX: /* arg 4 */ + case UNW_X86_64_R8: /* arg 5 */ + case UNW_X86_64_R9: /* arg 6 */ + return true; + break; + default: + return false; + } + } + return false; +} + +const char *RemoteRegisterMap::ip_register_name () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return "rip"; + case UNW_TARGET_I386: + return "eip"; + default: + ABORT("unsupported architecture"); + } + return NULL; +} + +const char *RemoteRegisterMap::sp_register_name () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return "rsp"; + case UNW_TARGET_I386: + return "esp"; + default: + ABORT("unsupported architecture"); + } + return NULL; +} + +int RemoteRegisterMap::caller_regno_for_ip () { + int callerr; + if (this->name_to_caller_regno (this->ip_register_name(), callerr)) + return callerr; + return -1; +} + +int RemoteRegisterMap::caller_regno_for_sp () { + int callerr; + if (this->name_to_caller_regno (this->sp_register_name(), callerr)) + return callerr; + return -1; +} + +int RemoteRegisterMap::unwind_regno_for_frame_pointer () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return UNW_X86_64_RBP; + case UNW_TARGET_I386: + return UNW_X86_EBP; + default: + ABORT("cannot be reached"); + } + return -1; +} + +int RemoteRegisterMap::unwind_regno_for_stack_pointer () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return UNW_X86_64_RSP; + case UNW_TARGET_I386: + return UNW_X86_ESP; + default: + ABORT("cannot be reached"); + } + return -1; +} + +// This call requires a "arg" which specifies a given process/thread to +// complete unlike the rest of the RegisterMap functions. Ideally this +// would be in the ctor but the register map is created when an +// AddressSpace is created and we don't have a process/thread yet. + +void RemoteRegisterMap::scan_caller_regs (unw_addr_space_t as, void *arg) { + for (int i = 0; i < 256; i++) { + unw_regtype_t type; + char namebuf[16]; + if (fAccessors.reg_info (as, i, &type, namebuf, sizeof (namebuf), arg) == UNW_ESUCCESS + && type != UNW_NOT_A_REG) { + std::vector::iterator j; + for (j = fRegMap.begin(); j != fRegMap.end(); ++j) { + if (strcasecmp (j->name, namebuf) == 0) { + j->caller_regno = i; + // if we haven't picked up a reg type yet it will be UNW_NOT_A_REG via the ctor + if (j->type == UNW_NOT_A_REG) + j->type = type; + if (j->type != type) { + ABORT("Caller and libunwind disagree about type of register"); + break; + } + } + } + // caller knows about a register we don't have a libunwind entry for + if (j == fRegMap.end()) { + RemoteRegisterMap::reg r; + r.name = strdup (namebuf); + r.caller_regno = i; + r.type = type; + fRegMap.push_back(r); + } + } + } +} + + +bool RemoteRegisterMap::name_to_unwind_regno (const char *name, int& unwindr) { + if (name == NULL) + return false; + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (strcasecmp (j->name, name) == 0) { + unwindr = j->unwind_regno; + return true; + } + return false; +} + +bool RemoteRegisterMap::unwind_regno_to_machine_regno (int unwindr, int& machiner) { + if (unwindr == UNW_REG_IP) + unwindr = this->caller_regno_for_ip (); + if (unwindr == UNW_REG_SP) + unwindr = this->caller_regno_for_sp (); + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->unwind_regno == unwindr && j->machine_regno != -1) { + machiner = j->machine_regno; + return true; + } + return false; +} +bool RemoteRegisterMap::machine_regno_to_unwind_regno (int machr, int& unwindr) { + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->machine_regno == machr && j->unwind_regno != -1) { + unwindr = j->unwind_regno; + return true; + } + return false; +} +bool RemoteRegisterMap::caller_regno_to_unwind_regno (int callerr, int& unwindr) { + if (this->caller_regno_for_ip() == callerr) { + unwindr = UNW_REG_IP; + return true; + } + if (this->caller_regno_for_sp() == callerr) { + unwindr = UNW_REG_SP; + return true; + } + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->caller_regno == callerr && j->unwind_regno != -1) { + unwindr = j->unwind_regno; + return true; + } + return false; +} + +const char* RemoteRegisterMap::unwind_regno_to_name (int unwindr) { + for (std::vector::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->unwind_regno == unwindr && j->name != NULL) { + return j->name; + } + return NULL; +} + +int RemoteRegisterMap::byte_size_for_regtype (unw_regtype_t type) { + switch (type) { + case UNW_TARGET_X86_64: + case UNW_TARGET_I386: + if (type == UNW_INTEGER_REG) return fWordSize; + if (type == UNW_FLOATING_POINT_REG) return 8; + if (type == UNW_VECTOR_REG) return 16; + default: + ABORT("cannot be reached"); + } + return -1; +} + + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING + +#endif // __REMOTE_REGISTER_MAP_HPP__ + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h new file mode 100644 index 000000000000..b03551cd21ca --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h @@ -0,0 +1,85 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteUnwindProfile.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_PROFILE_H__ +#define __UNWIND_PROFILE_H__ +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include + +// The architecture-independent profile of a function's prologue + +namespace lldb_private +{ + +class RemoteUnwindProfile { +public: + RemoteUnwindProfile () : fRegistersSaved(32, 0), fRegSizes(10, 0) { } + struct CFALocation { + int regno; + int offset; + }; + enum RegisterSavedWhere { kRegisterOffsetFromCFA, kRegisterIsCFA }; + enum RegisterType { kGeneralPurposeRegister = 0, kFloatingPointRegister, kVectorRegister }; + struct SavedReg { + int regno; + RegisterSavedWhere location; + int64_t value; + int adj; // Used in kRegisterInRegister e.g. when we recover the caller's rsp by + // taking the contents of rbp and subtracting 16. + RegisterType type; + }; + // In the following maps the key is the address after which this change has effect. + // + // 0 push %rbp + // 1 mov %rsp, %rbp + // 2 sub $16, %rsp + // + // At saved_registers<2> we'll find the record stating that rsp is now stored in rbp. + + std::map cfa; + std::map > saved_registers; + + struct CFALocation initial_cfa; // At entry to the function + + std::vector fRegistersSaved; + std::vector fRegSizes; + SavedReg returnAddress; + uint64_t fStart, fEnd; // low and high pc values for this function. + // END is the addr of the first insn outside the function. + uint64_t fFirstInsnPastPrologue; +}; + +class RemoteProcInfo; + +bool AssemblyParse (RemoteProcInfo *procinfo, unw_accessors_t *as, unw_addr_space_t as, uint64_t start, uint64_t end, RemoteUnwindProfile &profile, void *arg); + + +class FuncBounds { + public: + FuncBounds (uint64_t low, uint64_t high) : fStart(low), fEnd(high) { } + uint64_t fStart; + uint64_t fEnd; +}; + +inline bool operator<(const FuncBounds &ap1, const FuncBounds &ap2) { + if (ap1.fStart < ap2.fStart) + return true; + if (ap1.fStart == ap2.fStart && ap1.fEnd < ap2.fEnd) + return true; + return false; +} + + +}; +#endif + + +#endif // __UNWIND_PROFILE_H__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c b/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c new file mode 100644 index 000000000000..584528353a41 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c @@ -0,0 +1,466 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- Unwind-sjlj.c -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * + * Implements setjump-longjump based C++ exceptions + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "unwind.h" +#include "InternalMacros.h" + +// +// ARM uses setjump/longjump based C++ exceptions. +// Other architectures use "zero cost" exceptions. +// +// With SJLJ based exceptions any function that has a catch clause or needs to do any clean up when +// an exception propagates through it, needs to call _Unwind_SjLj_Register() at the start of the +// function and _Unwind_SjLj_Unregister() at the end. The register function is called with the +// address of a block of memory in the function's stack frame. The runtime keeps a linked list +// (stack) of these blocks - one per thread. The calling function also sets the personality +// and lsda fields of the block. +// +// +#if __arm__ + +struct _Unwind_FunctionContext +{ + // next function in stack of handlers + struct _Unwind_FunctionContext* prev; + + // set by calling function before registering to be the landing pad + uintptr_t resumeLocation; + + // set by personality handler to be parameters passed to landing pad function + uintptr_t resumeParameters[4]; + + // set by calling function before registering + __personality_routine personality; // arm offset=24 + uintptr_t lsda; // arm offset=28 + + // variable length array, contains registers to restore + // 0 = r7, 1 = pc, 2 = sp + void* jbuf[]; +}; + + +#if FOR_DYLD + // implemented in dyld + extern struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack(); + extern void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc); +#else + static pthread_key_t sPerThreadTopOfFunctionStack = 0; + static pthread_once_t sOnceFlag = PTHREAD_ONCE_INIT; + + static void __Unwind_SjLj_MakeTopOfFunctionStackKey() + { + pthread_key_create(&sPerThreadTopOfFunctionStack, NULL); + } + + static struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack() + { + pthread_once(&sOnceFlag, __Unwind_SjLj_MakeTopOfFunctionStackKey); + return (struct _Unwind_FunctionContext*)pthread_getspecific(sPerThreadTopOfFunctionStack); + } + + static void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc) + { + pthread_once(&sOnceFlag, __Unwind_SjLj_MakeTopOfFunctionStackKey); + pthread_setspecific(sPerThreadTopOfFunctionStack, fc); + } +#endif + + +// +// Called at start of each function that catches exceptions +// +EXPORT void _Unwind_SjLj_Register(struct _Unwind_FunctionContext* fc) +{ + fc->prev = __Unwind_SjLj_GetTopOfFunctionStack(); + __Unwind_SjLj_SetTopOfFunctionStack(fc); +} + + +// +// Called at end of each function that catches exceptions +// +EXPORT void _Unwind_SjLj_Unregister(struct _Unwind_FunctionContext* fc) +{ + __Unwind_SjLj_SetTopOfFunctionStack(fc->prev); +} + + +static _Unwind_Reason_Code unwind_phase1(struct _Unwind_Exception* exception_object) +{ + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + DEBUG_PRINT_UNWINDING("unwind_phase1: initial function-context=%p\n", c); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; c = c->prev) { + + // check for no more frames + if ( c == NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + + DEBUG_PRINT_UNWINDING("unwind_phase1: function-context=%p\n", c); + // if there is a personality routine, ask it if it will want to stop at this frame + if ( c->personality != NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, c->personality); + _Unwind_Reason_Code personalityResult = (*c->personality)(1, _UA_SEARCH_PHASE, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c); + switch ( personalityResult ) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember function context + handlerNotFound = false; + exception_object->private_2 = (uintptr_t)c; + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + // continue unwinding + break; + + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code unwind_phase2(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while ( true ) { + DEBUG_PRINT_UNWINDING("unwind_phase2s(ex_ojb=%p): function-context=%p\n", exception_object, c); + + // check for no more frames + if ( c == NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + + // if there is a personality routine, tell it we are unwinding + if ( c->personality != NULL ) { + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ( (uintptr_t)c == exception_object->private_2 ) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); // tell personality this was the frame it marked in phase 1 + _Unwind_Reason_Code personalityResult = (*c->personality)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + if ( (uintptr_t)c == exception_object->private_2 ) { + // phase 1 said we would stop at this frame, but we did not... + ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT, will resume at landing pad %p\n", exception_object, c->jbuf[1]); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + // unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code unwind_phase2_forced(struct _Unwind_Exception* exception_object, + _Unwind_Stop_Fn stop, void* stop_parameter) +{ + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while ( true ) { + + // get next frame (skip over first which is _Unwind_RaiseException) + if ( c == NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + + // call stop function at each frame + _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = (*stop)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c, stop_parameter); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult); + if ( stopResult != _URC_NO_REASON ) { + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if ( c->personality != NULL ) { + __personality_routine p = (__personality_routine)c->personality; + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p); + _Unwind_Reason_Code personalityResult = (*p)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + break; + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n", + exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // call stop function one last time and tell it we've reached the end of the stack + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", exception_object); + _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE|_UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context*)c, stop_parameter); + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +// +// Called by __cxa_throw. Only returns if there is a fatal error +// +EXPORT _Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_SjLj_RaiseException(ex_obj=%p)\n", exception_object); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(exception_object); + if ( phase1 != _URC_NO_REASON ) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(exception_object); +} + + +// +// When _Unwind_RaiseException() is in phase2, it hands control +// to the personality function at each frame. The personality +// may force a jump to a landing pad in that function, the landing +// pad code may then call _Unwind_Resume() to continue with the +// unwinding. Note: the call to _Unwind_Resume() is from compiler +// geneated user code. All other _Unwind_* routines are called +// by the C++ runtime __cxa_* routines. +// +// Re-throwing an exception is implemented by having the code call +// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +// +EXPORT void _Unwind_SjLj_Resume(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_SjLj_Resume(ex_obj=%p)\n", exception_object); + + if ( exception_object->private_1 != 0 ) + unwind_phase2_forced(exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2); + else + unwind_phase2(exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + ABORT("_Unwind_SjLj_Resume() can't return"); +} + + +// +// Called by __cxa_rethrow() +// +EXPORT _Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n", exception_object, exception_object->private_1); + // if this is non-forced and a stopping place was found, then this is a re-throw + // call _Unwind_RaiseException() as if this was a new exception + if ( exception_object->private_1 == 0 ) + _Unwind_SjLj_RaiseException(exception_object); + + // call through to _Unwind_Resume() which distiguishes between forced and regular exceptions + _Unwind_SjLj_Resume(exception_object); + ABORT("__Unwind_SjLj_Resume_or_Rethrow() called _Unwind_SjLj_Resume() which unexpectedly returned"); +} + + +// +// Called by personality handler during phase 2 to get LSDA for current frame +// +EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context) +{ + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%0lX\n", context, ufc->lsda); + return ufc->lsda; +} + + +// +// Called by personality handler during phase 2 to get register values +// +EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index) +{ + DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d)\n", context, index); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + return ufc->resumeParameters[index]; +} + + + +// +// Called by personality handler during phase 2 to alter register values +// +EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0lX)\n", context, index, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + ufc->resumeParameters[index] = new_value; +} + + +// +// Called by personality handler during phase 2 to get instruction pointer +// +EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context) +{ + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%lX\n", context, ufc->resumeLocation+1); + return ufc->resumeLocation+1; +} + +// +// Called by personality handler during phase 2 to get instruction pointer +// ipBefore is a boolean that says if IP is already adjusted to be the call +// site address. Normally IP is the return address. +// +EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore) +{ + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + *ipBefore = 0; + DEBUG_PRINT_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%lX\n", context, ipBefore, ufc->resumeLocation+1); + return ufc->resumeLocation+1; +} + + +// +// Called by personality handler during phase 2 to alter instruction pointer +// +EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0lX)\n", context, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + ufc->resumeLocation = new_value-1; +} + +// +// Called by personality handler during phase 2 to find the start of the function +// +EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context) +{ + // Not supported or needed for sjlj based unwinding + DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p)\n", context); + return 0; +} + +// +// Called by personality handler during phase 2 if a foreign exception is caught +// +EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object); + if ( exception_object->exception_cleanup != NULL ) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object); +} + + +// +// Called by personality handler during phase 2 to get base address for data relative encodings +// +EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) +{ + // Not supported or needed for sjlj based unwinding + DEBUG_PRINT_API("_Unwind_GetDataRelBase(context=%p)\n", context); + ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +// +// Called by personality handler during phase 2 to get base address for text relative encodings +// +EXPORT uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) +{ + // Not supported or needed for sjlj based unwinding + DEBUG_PRINT_API("_Unwind_GetTextRelBase(context=%p)\n", context); + ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + + +// +// Called by personality handler to get Call Frame Area for current frame +// +EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context* context) +{ + DEBUG_PRINT_API("_Unwind_GetCFA(context=%p)\n", context); + if ( context != NULL ) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + // setjmp/longjmp based exceptions don't have a true CFA + // the SP in the jmpbuf is the closest approximation + return (uintptr_t)ufc->jbuf[2]; + } + return 0; +} + + + + + +#endif // __arm__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp new file mode 100644 index 000000000000..e940b8b8bf1c --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp @@ -0,0 +1,1307 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- UnwindCursor.hpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __UNWINDCURSOR_HPP__ +#define __UNWINDCURSOR_HPP__ + +#include +#include +#include +#include +#include + +#include "libunwind.h" + +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfInstructions.hpp" + +#include "AssemblyParser.hpp" +#include "AssemblyInstructions.hpp" +#include "RemoteProcInfo.hpp" +#include "ArchDefaultUnwinder.hpp" +#include "RemoteDebuggerDummyUnwinder.hpp" + +#include "CompactUnwinder.hpp" +#include "InternalMacros.h" + +// private keymgr stuff +#define KEYMGR_GCC3_DW2_OBJ_LIST 302 +extern "C" { + extern void _keymgr_set_and_unlock_processwide_ptr(int key, void* ptr); + extern void* _keymgr_get_and_lock_processwide_ptr(int key); +}; + +// undocumented libgcc "struct object" +struct libgcc_object +{ + void* start; + void* unused1; + void* unused2; + void* fde; + unsigned long encoding; + void* fde_end; + libgcc_object* next; +}; + +// undocumented libgcc "struct km_object_info" referenced by KEYMGR_GCC3_DW2_OBJ_LIST +struct libgcc_object_info { + struct libgcc_object* seen_objects; + struct libgcc_object* unseen_objects; + unsigned spare[2]; +}; + + + + +namespace lldb_private { + +#if !FOR_DYLD +template +class DwarfFDECache +{ +public: + typedef typename A::pint_t pint_t; + static pint_t findFDE(pint_t mh, pint_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void removeAllIn(pint_t mh); + static void iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); +private: + static void dyldUnloadHook(const struct mach_header* mh, intptr_t vmaddr_slide); + + struct entry { pint_t mh; pint_t ip_start; pint_t ip_end; pint_t fde; }; + + // these fields are all static to avoid needing an initializer + // there is only one instance of this class per process + static pthread_rwlock_t fgLock; + static bool fgRegisteredForDyldUnloads; + // can't use std::vector<> here because this code must live in libSystem.dylib (which is below libstdc++.dylib) + static entry* fgBuffer; + static entry* fgBufferUsed; + static entry* fgBufferEnd; + static entry fgInitialBuffer[64]; +}; + +template typename DwarfFDECache::entry* DwarfFDECache::fgBuffer = fgInitialBuffer; +template typename DwarfFDECache::entry* DwarfFDECache::fgBufferUsed = fgInitialBuffer; +template typename DwarfFDECache::entry* DwarfFDECache::fgBufferEnd = &fgInitialBuffer[64]; +template typename DwarfFDECache::entry DwarfFDECache::fgInitialBuffer[64]; + +template +pthread_rwlock_t DwarfFDECache::fgLock = PTHREAD_RWLOCK_INITIALIZER; + +template +bool DwarfFDECache::fgRegisteredForDyldUnloads = false; + + +template +typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) +{ + pint_t result = NULL; + DEBUG_LOG_NON_ZERO(::pthread_rwlock_rdlock(&fgLock)); + for(entry* p=fgBuffer; p < fgBufferUsed; ++p) { + if ( (mh == p->mh) || (mh == 0) ) { + if ( (p->ip_start <= pc) && (pc < p->ip_end) ) { + result = p->fde; + break; + } + } + } + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); + //fprintf(stderr, "DwarfFDECache::findFDE(mh=0x%llX, pc=0x%llX) => 0x%llX\n", (uint64_t)mh, (uint64_t)pc, (uint64_t)result); + return result; +} + +template +void DwarfFDECache::add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde) +{ + //fprintf(stderr, "DwarfFDECache::add(mh=0x%llX, ip_start=0x%llX, ip_end=0x%llX, fde=0x%llX) pthread=%p\n", + // (uint64_t)mh, (uint64_t)ip_start, (uint64_t)ip_end, (uint64_t)fde, pthread_self()); + DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock)); + if ( fgBufferUsed >= fgBufferEnd ) { + int oldSize = fgBufferEnd - fgBuffer; + int newSize = oldSize*4; + entry* newBuffer = (entry*)malloc(newSize*sizeof(entry)); // can't use operator new in libSystem.dylib + memcpy(newBuffer, fgBuffer, oldSize*sizeof(entry)); + //fprintf(stderr, "DwarfFDECache::add() growing buffer to %d\n", newSize); + if ( fgBuffer != fgInitialBuffer ) + free(fgBuffer); + fgBuffer = newBuffer; + fgBufferUsed = &newBuffer[oldSize]; + fgBufferEnd = &newBuffer[newSize]; + } + fgBufferUsed->mh = mh; + fgBufferUsed->ip_start = ip_start; + fgBufferUsed->ip_end = ip_end; + fgBufferUsed->fde = fde; + ++fgBufferUsed; +#if !defined (SUPPORT_REMOTE_UNWINDING) + if ( !fgRegisteredForDyldUnloads ) { + _dyld_register_func_for_remove_image(&dyldUnloadHook); + fgRegisteredForDyldUnloads = true; + } +#endif + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); +} + + + +template +void DwarfFDECache::removeAllIn(pint_t mh) +{ + DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock)); + entry* d=fgBuffer; + for(const entry* s=fgBuffer; s < fgBufferUsed; ++s) { + if ( s->mh != mh ) { + if ( d != s ) + *d = *s; + ++d; + } + } + fgBufferUsed = d; + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); +} + + +template +void DwarfFDECache::dyldUnloadHook(const struct mach_header* mh, intptr_t vmaddr_slide) +{ +#if !defined (SUPPORT_REMOTE_UNWINDING) + removeAllIn((pint_t)mh); +#endif +} + +template +void DwarfFDECache::iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) +{ + DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock)); + for(entry* p=fgBuffer; p < fgBufferUsed; ++p) { + (*func)(p->ip_start, p->ip_end, p->fde, p->mh); + } + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); +} +#endif // !FOR_DYLD + + + + +#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field)) + +template +class UnwindSectionHeader { +public: + UnwindSectionHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t version() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, version)); } + uint32_t commonEncodingsArraySectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, commonEncodingsArraySectionOffset)); } + uint32_t commonEncodingsArrayCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, commonEncodingsArrayCount)); } + uint32_t personalityArraySectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, personalityArraySectionOffset)); } + uint32_t personalityArrayCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, personalityArrayCount)); } + uint32_t indexSectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, indexSectionOffset)); } + uint32_t indexCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, indexCount)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + +template +class UnwindSectionIndexArray { +public: + UnwindSectionIndexArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, functionOffset)); } + uint32_t secondLevelPagesSectionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, secondLevelPagesSectionOffset)); } + uint32_t lsdaIndexArraySectionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, lsdaIndexArraySectionOffset)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template +class UnwindSectionRegularPageHeader { +public: + UnwindSectionRegularPageHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t kind() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_regular_second_level_page_header, kind)); } + uint16_t entryPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_regular_second_level_page_header, entryPageOffset)); } + uint16_t entryCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_regular_second_level_page_header, entryCount)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template +class UnwindSectionRegularArray { +public: + UnwindSectionRegularArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_regular_second_level_entry, index, functionOffset)); } + uint32_t encoding(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template +class UnwindSectionCompressedPageHeader { +public: + UnwindSectionCompressedPageHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t kind() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_compressed_second_level_page_header, kind)); } + uint16_t entryPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, entryPageOffset)); } + uint16_t entryCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); } + uint16_t encodingsPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, encodingsPageOffset)); } + uint16_t encodingsCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, encodingsCount)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template +class UnwindSectionCompressedArray { +public: + UnwindSectionCompressedArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( fAddressSpace.get32(fAddr + index*sizeof(uint32_t)) ); } + uint16_t encodingIndex(int index) const INLINE { return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( fAddressSpace.get32(fAddr + index*sizeof(uint32_t)) ); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template +class UnwindSectionLsdaArray { +public: + UnwindSectionLsdaArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, index, functionOffset)); } + int32_t lsdaOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, index, lsdaOffset)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template +class UnwindCursor +{ +public: + UnwindCursor(unw_context_t* context, A& as); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual uint64_t getReg(int); + virtual int getReg(int, uint64_t*); + virtual int setReg(int, uint64_t); + virtual bool validFloatReg(int); + virtual double getFloatReg(int); + virtual int getFloatReg(int, double*); + virtual int setFloatReg(int, double); + virtual int step(); + virtual void getInfo(unw_proc_info_t*); + virtual void jumpto(); + virtual const char* getRegisterName(int num); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char* buf, size_t bufLen, unw_word_t* offset); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress=false); + + void operator delete(void* p, size_t size) {} + +protected: + typedef typename A::pint_t pint_t; + typedef uint32_t EncodedUnwindInfo; + + virtual bool getInfoFromCompactEncodingSection(pint_t pc, pint_t mh, pint_t unwindSectionStart); + virtual bool getInfoFromDwarfSection(pint_t pc, pint_t mh, pint_t ehSectionStart, uint32_t sectionLength, uint32_t sectionOffsetOfFDE); + + virtual int stepWithDwarfFDE() + { return DwarfInstructions::stepWithDwarf(fAddressSpace, this->getReg(UNW_REG_IP), fInfo.unwind_info, fRegisters); } + + virtual int stepWithCompactEncoding() { R dummy; return stepWithCompactEncoding(dummy); } + int stepWithCompactEncoding(Registers_x86_64&) + { return CompactUnwinder_x86_64::stepWithCompactEncoding(fInfo.format, fInfo.start_ip, fAddressSpace, fRegisters); } + int stepWithCompactEncoding(Registers_x86&) + { return CompactUnwinder_x86::stepWithCompactEncoding(fInfo.format, fInfo.start_ip, fAddressSpace, fRegisters); } + int stepWithCompactEncoding(Registers_ppc&) + { return UNW_EINVAL; } + +#if FOR_DYLD + #if __ppc__ + virtual bool mustUseDwarf() const { return true; } + #else + virtual bool mustUseDwarf() const { return false; } + #endif +#else + virtual bool mustUseDwarf() const { R dummy; uint32_t offset; return dwarfWithOffset(dummy, offset); } +#endif + + virtual bool dwarfWithOffset(uint32_t& offset) const { R dummy; return dwarfWithOffset(dummy, offset); } + virtual bool dwarfWithOffset(Registers_x86_64&, uint32_t& offset) const { + if ( (fInfo.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { + offset = (fInfo.format & UNWIND_X86_64_DWARF_SECTION_OFFSET); + return true; + } +#if SUPPORT_OLD_BINARIES + if ( (fInfo.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_COMPATIBILITY ) { + if ( (fInfo.format & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) { + offset = 0; + return true; + } + } +#endif + return false; + } + virtual bool dwarfWithOffset(Registers_x86&, uint32_t& offset) const { + if ( (fInfo.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { + offset = (fInfo.format & UNWIND_X86_DWARF_SECTION_OFFSET); + return true; + } +#if SUPPORT_OLD_BINARIES + if ( (fInfo.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_COMPATIBILITY ) { + if ( (fInfo.format & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) { + offset = 0; + return true; + } + } +#endif + return false; + } + virtual bool dwarfWithOffset(Registers_ppc&, uint32_t& offset) const { return true; } + + + virtual compact_unwind_encoding_t dwarfEncoding() const { R dummy; return dwarfEncoding(dummy); } + virtual compact_unwind_encoding_t dwarfEncoding(Registers_x86_64&) const { return UNWIND_X86_64_MODE_DWARF; } + virtual compact_unwind_encoding_t dwarfEncoding(Registers_x86&) const { return UNWIND_X86_MODE_DWARF; } + virtual compact_unwind_encoding_t dwarfEncoding(Registers_ppc&) const { return 0; } + + unw_proc_info_t fInfo; + R fRegisters; + A& fAddressSpace; + bool fUnwindInfoMissing; + bool fIsSignalFrame; +}; + +typedef UnwindCursor AbstractUnwindCursor; + +template +UnwindCursor::UnwindCursor(unw_context_t* context, A& as) + : fRegisters(context), fAddressSpace(as), fUnwindInfoMissing(false), fIsSignalFrame(false) +{ + COMPILE_TIME_ASSERT( sizeof(UnwindCursor) < sizeof(unw_cursor_t) ); + + bzero(&fInfo, sizeof(fInfo)); +} + +template +bool UnwindCursor::validReg(int regNum) +{ + return fRegisters.validRegister(regNum); +} + +template +uint64_t UnwindCursor::getReg(int regNum) +{ + return fRegisters.getRegister(regNum); +} + +template +int UnwindCursor::getReg(int regNum, uint64_t *valp) +{ + *valp = fRegisters.getRegister(regNum); + return UNW_ESUCCESS; +} + +template +int UnwindCursor::setReg(int regNum, uint64_t value) +{ + fRegisters.setRegister(regNum, value); + return UNW_ESUCCESS; +} + +template +bool UnwindCursor::validFloatReg(int regNum) +{ + return fRegisters.validFloatRegister(regNum); +} + +template +double UnwindCursor::getFloatReg(int regNum) +{ + return fRegisters.getFloatRegister(regNum); +} + +template +int UnwindCursor::getFloatReg(int regNum, double *valp) +{ + *valp = fRegisters.getFloatRegister(regNum); + return UNW_ESUCCESS; +} + +template +int UnwindCursor::setFloatReg(int regNum, double value) +{ + fRegisters.setFloatRegister(regNum, value); + return UNW_ESUCCESS; +} + +template +void UnwindCursor::jumpto() +{ +#if !defined (SUPPORT_REMOTE_UNWINDING) + fRegisters.jumpto(); +#endif +} + +template +const char* UnwindCursor::getRegisterName(int regNum) +{ + return fRegisters.getRegisterName(regNum); +} + +template +bool UnwindCursor::isSignalFrame() +{ + return fIsSignalFrame; +} + + +template +bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, pint_t mh, pint_t ehSectionStart, uint32_t sectionLength, uint32_t sectionOffsetOfFDE) +{ + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + bool foundFDE = false; + bool foundInCache = false; + // if compact encoding table gave offset into dwarf section, go directly there + if ( sectionOffsetOfFDE != 0 ) { + foundFDE = CFI_Parser::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, ehSectionStart+sectionOffsetOfFDE, &fdeInfo, &cieInfo); + } +#if !FOR_DYLD + if ( !foundFDE ) { + // otherwise, search cache of previously found FDEs + pint_t cachedFDE = DwarfFDECache::findFDE(mh, pc); + //fprintf(stderr, "getInfoFromDwarfSection(pc=0x%llX) cachedFDE=0x%llX\n", (uint64_t)pc, (uint64_t)cachedFDE); + if ( cachedFDE != 0 ) { + foundFDE = CFI_Parser::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, cachedFDE, &fdeInfo, &cieInfo); + foundInCache = foundFDE; + //fprintf(stderr, "cachedFDE=0x%llX, foundInCache=%d\n", (uint64_t)cachedFDE, foundInCache); + } + } +#endif + if ( !foundFDE ) { + // still not found, do full scan of __eh_frame section + foundFDE = CFI_Parser::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, 0, &fdeInfo, &cieInfo); + } + if ( foundFDE ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + // save off parsed FDE info + fInfo.start_ip = fdeInfo.pcStart; + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function + fInfo.flags = 0; + fInfo.format = dwarfEncoding(); + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; + fInfo.extra = (unw_word_t)mh; + if ( !foundInCache && (sectionOffsetOfFDE == 0) ) { + // don't add to cache entries the compact encoding table can find quickly + //fprintf(stderr, "getInfoFromDwarfSection(pc=0x%0llX), mh=0x%llX, start_ip=0x%0llX, fde=0x%0llX, personality=0x%0llX\n", + // (uint64_t)pc, (uint64_t)mh, fInfo.start_ip, fInfo.unwind_info, fInfo.handler); +#if !FOR_DYLD + DwarfFDECache::add(mh, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart); +#endif + } + return true; + } + } + //DEBUG_MESSAGE("can't find/use FDE for pc=0x%llX\n", (uint64_t)pc); + return false; +} + +template +bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, pint_t mh, pint_t unwindSectionStart) +{ + const bool log = false; + if ( log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", (uint64_t)pc, (uint64_t)mh); + + const UnwindSectionHeader sectionHeader(fAddressSpace, unwindSectionStart); + if ( sectionHeader.version() != UNWIND_SECTION_VERSION ) + return false; + + // do a binary search of top level index to find page with unwind info + uint32_t targetFunctionOffset = pc - mh; + const UnwindSectionIndexArray topIndex(fAddressSpace, unwindSectionStart + sectionHeader.indexSectionOffset()); + uint32_t low = 0; + uint32_t high = sectionHeader.indexCount(); + const uint32_t last = high - 1; + while ( low < high ) { + uint32_t mid = (low + high)/2; + //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", mid, low, high, topIndex.functionOffset(mid)); + if ( topIndex.functionOffset(mid) <= targetFunctionOffset ) { + if ( (mid == last) || (topIndex.functionOffset(mid+1) > targetFunctionOffset) ) { + low = mid; + break; + } + else { + low = mid+1; + } + } + else { + high = mid; + } + } + const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low); + const uint32_t firstLevelNextPageFunctionOffset = topIndex.functionOffset(low+1); + const pint_t secondLevelAddr = unwindSectionStart+topIndex.secondLevelPagesSectionOffset(low); + const pint_t lsdaArrayStartAddr = unwindSectionStart+topIndex.lsdaIndexArraySectionOffset(low); + const pint_t lsdaArrayEndAddr = unwindSectionStart+topIndex.lsdaIndexArraySectionOffset(low+1); + if ( log ) fprintf(stderr, "\tfirst level search for result index=%d to secondLevelAddr=0x%llX\n", + low, (uint64_t)secondLevelAddr); + // do a binary search of second level page index + uint32_t encoding = 0; + pint_t funcStart = 0; + pint_t funcEnd = 0; + pint_t lsda = 0; + pint_t personality = 0; + uint32_t pageKind = fAddressSpace.get32(secondLevelAddr); + if ( pageKind == UNWIND_SECOND_LEVEL_REGULAR ) { + // regular page + UnwindSectionRegularPageHeader pageHeader(fAddressSpace, secondLevelAddr); + UnwindSectionRegularArray pageIndex(fAddressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + // binary search looks for entry with e where index[e].offset <= pc < index[e+1].offset + if ( log ) fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in regular page starting at secondLevelAddr=0x%llX\n", + (uint64_t)targetFunctionOffset, (uint64_t)secondLevelAddr); + uint32_t low = 0; + uint32_t high = pageHeader.entryCount(); + while ( low < high ) { + uint32_t mid = (low + high)/2; + if ( pageIndex.functionOffset(mid) <= targetFunctionOffset ) { + if ( mid == (uint32_t)(pageHeader.entryCount()-1) ) { + // at end of table + low = mid; + funcEnd = firstLevelNextPageFunctionOffset + mh; + break; + } + else if ( pageIndex.functionOffset(mid+1) > targetFunctionOffset ) { + // next is too big, so we found it + low = mid; + funcEnd = pageIndex.functionOffset(low+1) + mh; + break; + } + else { + low = mid+1; + } + } + else { + high = mid; + } + } + encoding = pageIndex.encoding(low); + funcStart = pageIndex.functionOffset(low) + mh; + if ( pc < funcStart ) { + if ( log ) fprintf(stderr, "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart, (uint64_t)funcEnd); + return false; + } + if ( pc > funcEnd ) { + if ( log ) fprintf(stderr, "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart, (uint64_t)funcEnd); + return false; + } + } + else if ( pageKind == UNWIND_SECOND_LEVEL_COMPRESSED ) { + // compressed page + UnwindSectionCompressedPageHeader pageHeader(fAddressSpace, secondLevelAddr); + UnwindSectionCompressedArray pageIndex(fAddressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + const uint32_t targetFunctionPageOffset = targetFunctionOffset - firstLevelFunctionOffset; + // binary search looks for entry with e where index[e].offset <= pc < index[e+1].offset + if ( log ) fprintf(stderr, "\tbinary search of compressed page starting at secondLevelAddr=0x%llX\n", (uint64_t)secondLevelAddr); + uint32_t low = 0; + const uint32_t last = pageHeader.entryCount() - 1; + uint32_t high = pageHeader.entryCount(); + while ( low < high ) { + uint32_t mid = (low + high)/2; + if ( pageIndex.functionOffset(mid) <= targetFunctionPageOffset ) { + if ( (mid == last) || (pageIndex.functionOffset(mid+1) > targetFunctionPageOffset) ) { + low = mid; + break; + } + else { + low = mid+1; + } + } + else { + high = mid; + } + } + funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + mh; + if ( low < last ) + funcEnd = pageIndex.functionOffset(low+1) + firstLevelFunctionOffset + mh; + else + funcEnd = firstLevelNextPageFunctionOffset + mh; + if ( pc < funcStart ) { + DEBUG_MESSAGE("malformed __unwind_info, pc=0x%llX not in second level compressed unwind table. funcStart=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart); + return false; + } + if ( pc > funcEnd ) { + DEBUG_MESSAGE("malformed __unwind_info, pc=0x%llX not in second level compressed unwind table. funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcEnd); + return false; + } + uint16_t encodingIndex = pageIndex.encodingIndex(low); + if ( encodingIndex < sectionHeader.commonEncodingsArrayCount() ) { + // encoding is in common table in section header + encoding = fAddressSpace.get32(unwindSectionStart+sectionHeader.commonEncodingsArraySectionOffset()+encodingIndex*sizeof(uint32_t)); + } + else { + // encoding is in page specific table + uint16_t pageEncodingIndex = encodingIndex-sectionHeader.commonEncodingsArrayCount(); + encoding = fAddressSpace.get32(secondLevelAddr+pageHeader.encodingsPageOffset()+pageEncodingIndex*sizeof(uint32_t)); + } + } + else { + DEBUG_MESSAGE("malformed __unwind_info at 0x%0llX bad second level page\n", (uint64_t)unwindSectionStart); + return false; + } + + // look up LSDA, if encoding says function has one + if ( encoding & UNWIND_HAS_LSDA ) { + UnwindSectionLsdaArray lsdaIndex(fAddressSpace, lsdaArrayStartAddr); + uint32_t funcStartOffset = funcStart - mh; + uint32_t low = 0; + uint32_t high = (lsdaArrayEndAddr-lsdaArrayStartAddr)/sizeof(unwind_info_section_header_lsda_index_entry); + // binary search looks for entry with exact match for functionOffset + if ( log ) fprintf(stderr, "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", funcStartOffset); + while ( low < high ) { + uint32_t mid = (low + high)/2; + if ( lsdaIndex.functionOffset(mid) == funcStartOffset ) { + lsda = lsdaIndex.lsdaOffset(mid) + mh; + break; + } + else if ( lsdaIndex.functionOffset(mid) < funcStartOffset ) { + low = mid+1; + } + else { + high = mid; + } + } + if ( lsda == 0 ) { + DEBUG_MESSAGE("found encoding 0x%08X with HAS_LSDA bit set for pc=0x%0llX, but lsda table has no entry\n", encoding, (uint64_t)pc); + return false; + } + } + + // extact personality routine, if encoding says function has one + uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> (__builtin_ctz(UNWIND_PERSONALITY_MASK)); + if ( personalityIndex != 0 ) { + --personalityIndex; // change 1-based to zero-based index + if ( personalityIndex > sectionHeader.personalityArrayCount() ) { + DEBUG_MESSAGE("found encoding 0x%08X with personality index %d, but personality table has only %d entires\n", + encoding, personalityIndex, sectionHeader.personalityArrayCount()); + return false; + } + int32_t personalityDelta = fAddressSpace.get32(unwindSectionStart+sectionHeader.personalityArraySectionOffset()+personalityIndex*sizeof(uint32_t)); + pint_t personalityPointer = personalityDelta + mh; + personality = fAddressSpace.getP(personalityPointer); + if (log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), personalityDelta=0x%08X, personality=0x%08llX\n", + (uint64_t)pc, personalityDelta, (uint64_t)personality); + } + + if (log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n", + (uint64_t)pc, encoding, (uint64_t)lsda, (uint64_t)funcStart); + fInfo.start_ip = funcStart; + fInfo.end_ip = funcEnd; + fInfo.lsda = lsda; + fInfo.handler = personality; + fInfo.gp = 0; + fInfo.flags = 0; + fInfo.format = encoding; + fInfo.unwind_info = 0; + fInfo.unwind_info_size = 0; + fInfo.extra = mh; + return true; +} + +template +void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) +{ + pint_t pc = this->getReg(UNW_REG_IP); + + // if the last line of a function is a "throw" the compile sometimes + // emits no instructions after the call to __cxa_throw. This means + // the return address is actually the start of the next function. + // To disambiguate this, back up the pc when we know it is a return + // address. + if ( isReturnAddress ) + --pc; + + // ask address space object to find unwind sections for this pc + pint_t mh; + pint_t dwarfStart; + pint_t dwarfLength; + pint_t compactStart; + if ( fAddressSpace.findUnwindSections(pc, mh, dwarfStart, dwarfLength, compactStart) ) { + // if there is a compact unwind encoding table, look there first + if ( compactStart != 0 ) { + if ( this->getInfoFromCompactEncodingSection(pc, mh, compactStart) ) { +#if !FOR_DYLD + // found info in table, done unless encoding says to use dwarf + uint32_t offsetInDwarfSection; + if ( (dwarfStart != 0) && dwarfWithOffset(offsetInDwarfSection) ) { + if ( this->getInfoFromDwarfSection(pc, mh, dwarfStart, dwarfLength, offsetInDwarfSection) ) { + // found info in dwarf, done + return; + } + } +#endif + // if unwind table has entry, but entry says there is no unwind info, note that + if ( fInfo.format == 0 ) + fUnwindInfoMissing = true; + + // old compact encoding + if ( !mustUseDwarf() ) { + return; + } + } + } +#if !FOR_DYLD || __ppc__ + // if there is dwarf unwind info, look there next + if ( dwarfStart != 0 ) { + if ( this->getInfoFromDwarfSection(pc, mh, dwarfStart, dwarfLength, 0) ) { + // found info in dwarf, done + return; + } + } +#endif + } + +#if !FOR_DYLD + // the PC is not in code loaded by dyld, look through __register_frame() registered FDEs + pint_t cachedFDE = DwarfFDECache::findFDE(0, pc); + if ( cachedFDE != 0 ) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + const char* msg = CFI_Parser::decodeFDE(fAddressSpace, cachedFDE, &fdeInfo, &cieInfo); + if ( msg == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + // save off parsed FDE info + fInfo.start_ip = fdeInfo.pcStart; + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function + fInfo.flags = 0; + fInfo.format = dwarfEncoding(); + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; + fInfo.extra = 0; + return; + } + } + } + +#if !defined (SUPPORT_REMOTE_UNWINDING) + // lastly check for old style keymgr registration of dynamically generated FDEs + + // acquire exclusive access to libgcc_object_info + libgcc_object_info* head = (libgcc_object_info*)_keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); + if ( head != NULL ) { + // look at each FDE in keymgr + for (libgcc_object* ob = head->unseen_objects; ob != NULL; ob = ob->next) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + const char* msg = CFI_Parser::decodeFDE(fAddressSpace, (pint_t)ob->fde, &fdeInfo, &cieInfo); + if ( msg == NULL ) { + // see if this FDE is for a function that includes the pc we are looking for + if ( (fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd) ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + // save off parsed FDE info + fInfo.start_ip = fdeInfo.pcStart; + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function + fInfo.flags = 0; + fInfo.format = dwarfEncoding(); + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; + fInfo.extra = 0; + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head); + return; + } + } + } + } + } + // release libgcc_object_info + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head); +#endif // !SUPPORT_REMOTE_UNWINDING + +#endif // !FOR_DYLD + + // no unwind info, flag that we can't reliable unwind + fUnwindInfoMissing = true; +} + + +template +int UnwindCursor::step() +{ + // bottom of stack is defined as when no more unwind info + if ( fUnwindInfoMissing ) + return UNW_STEP_END; + + // apply unwinding to register set + int result; + if ( this->mustUseDwarf() ) + result = this->stepWithDwarfFDE(); + else + result = this->stepWithCompactEncoding(); + + // update info based on new PC + if ( result == UNW_STEP_SUCCESS ) { + this->setInfoBasedOnIPRegister(true); + if ( fUnwindInfoMissing ) + return UNW_STEP_END; + } + + return result; +} + + +template +void UnwindCursor::getInfo(unw_proc_info_t* info) +{ + *info = fInfo; +} + + +template +bool UnwindCursor::getFunctionName(char* buf, size_t bufLen, unw_word_t* offset) +{ + return fAddressSpace.findFunctionName(this->getReg(UNW_REG_IP), buf, bufLen, offset); +} + +#if defined (SUPPORT_REMOTE_UNWINDING) +template +class RemoteUnwindCursor : UnwindCursor +{ +public: + typedef typename A::pint_t pint_t; + RemoteUnwindCursor(A& as, unw_context_t* regs, void* arg); + virtual bool validReg(int); + virtual int getReg(int r, uint64_t*); + virtual int setReg(int, uint64_t); + virtual bool validFloatReg(int); + virtual int getFloatReg(int, double*); + virtual int setFloatReg(int, double); + virtual const char* getRegisterName(int); + virtual int step(); + virtual void setRemoteContext(void*); + virtual bool remoteUnwindCursor () const {return this->fAddressSpace.getRemoteProcInfo() != NULL; } + virtual int endOfPrologueInsns(unw_word_t, unw_word_t, unw_word_t*); + void operator delete(void* p, size_t size) {} +private: + virtual bool caller_regno_to_unwind_regno (int, int&); + + bool fIsLeafFrame; + bool fIsFirstFrame; + void* fArg; +}; + +typedef RemoteUnwindCursor AbstractRemoteUnwindCursor; + +template +RemoteUnwindCursor::RemoteUnwindCursor(A& as, unw_context_t* regs, void* arg) + : UnwindCursor::UnwindCursor(regs, as), fIsFirstFrame (false), fIsLeafFrame(false), fArg(arg) +{ + COMPILE_TIME_ASSERT( sizeof(RemoteUnwindCursor) < sizeof(unw_cursor_t) ); +} + +template +bool RemoteUnwindCursor::validReg(int r) +{ + int unwind_regno; + if (!caller_regno_to_unwind_regno(r, unwind_regno)) + return false; + return UnwindCursor::fRegisters.validRegister(unwind_regno); +} + +template +int RemoteUnwindCursor::getReg(int regNum, uint64_t *valp) +{ + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("getRemoteReg called with a local unwind, use getReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // we always return nonvolatile registers. If we have the entire register state available + // for this frame then we can return any register requested. + if (regmap->nonvolatile_reg_p (regNum) == true || fIsLeafFrame == true) { + return this->UnwindCursor::getReg (unwind_regno, valp); + } + return UNW_EREGUNAVAILABLE; +} + +template +int RemoteUnwindCursor::setReg(int regNum, uint64_t val) +{ + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("setRemoteReg called with a local unwind, use setReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // Only allow the registers to be set if the unwind cursor is pointing to the + // first frame. We need to track where registers were retrieved from in memory + // in every other frame. Until then, we prohibit register setting in all but + // the first frame. + if (fIsFirstFrame) { + return this->setReg(unwind_regno, val); + } + return UNW_EREGUNAVAILABLE; +} + +template +bool RemoteUnwindCursor::validFloatReg(int r) +{ + int unwind_regno; + if (!caller_regno_to_unwind_regno(r, unwind_regno)) + return false; + return UnwindCursor::fRegisters.validFloatRegister(unwind_regno); +} + +template +int RemoteUnwindCursor::getFloatReg(int regNum, double *valp) +{ + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("getRemoteReg called with a local unwind, use getReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // we always return nonvolatile registers. If we have the entire register state available + // for this frame then we can return any register requested. + if (regmap->nonvolatile_reg_p (regNum) == true || fIsLeafFrame == true) { + return this->UnwindCursor::getFloatReg (unwind_regno, valp); + } + return UNW_EREGUNAVAILABLE; +} + +template +int RemoteUnwindCursor::setFloatReg(int regNum, double val) +{ + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("setRemoteReg called with a local unwind, use setReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // Only allow the registers to be set if the unwind cursor is pointing to the + // first frame. We need to track where registers were retrieved from in memory + // in every other frame. Until then, we prohibit register setting in all but + // the first frame. + if (fIsFirstFrame) { + return this->setFloatReg(unwind_regno, val); + } + return UNW_EREGUNAVAILABLE; +} + + +template +const char* RemoteUnwindCursor::getRegisterName(int r) +{ + int t; + if (!this->caller_regno_to_unwind_regno(r, t)) + return NULL; + r = t; + return this->UnwindCursor::getRegisterName(r); +} + +template +int RemoteUnwindCursor::step() +{ + pint_t pc = this->UnwindCursor::getReg(UNW_REG_IP); + pint_t sp = this->UnwindCursor::getReg(UNW_REG_SP); + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo(); + bool frame_is_sigtramp = false; + bool frame_is_inferior_function_call_dummy = false; + + if (procinfo == NULL) { + ABORT("stepRemote called with local unwind, use step() instead."); + return UNW_EUNSPEC; + } + struct timeval *step_remote = procinfo->timestamp_start(); + procinfo->logVerbose ("stepRemote stepping out of frame with pc value 0x%llx", pc); + + // We'll be off of the first frame once we finish this step. + fIsFirstFrame = false; + + if (UnwindCursor::fAddressSpace.accessors() + && UnwindCursor::fAddressSpace.accessors()->proc_is_sigtramp != NULL + && UnwindCursor::fAddressSpace.accessors()->proc_is_sigtramp (procinfo->wrap(), pc, fArg)) { + frame_is_sigtramp = true; + } + if (UnwindCursor::fAddressSpace.accessors() + && UnwindCursor::fAddressSpace.accessors()->proc_is_inferior_function_call != NULL + && UnwindCursor::fAddressSpace.accessors()->proc_is_inferior_function_call (procinfo->wrap(), pc, sp, fArg)) { + frame_is_inferior_function_call_dummy = true; + } + + // If the function we're unwinding can't be a leaf function, + // use the eh_frame or compact unwind info if possible. + // The caller should pass couldBeLeafFunc == 0 on the first step of a new context + // but we can't trust them in that. + + if ((fIsLeafFrame == false && frame_is_inferior_function_call_dummy == false) + || frame_is_sigtramp) { + R saved_registers(UnwindCursor::fRegisters); + this->setInfoBasedOnIPRegister(true); + // bottom of stack is defined as when no more unwind info + if ( !UnwindCursor::fUnwindInfoMissing ) { + int result; + const char *method; + if ( this->mustUseDwarf() ) { + result = this->stepWithDwarfFDE(); + method = "dwarf"; + } + else { + result = this->stepWithCompactEncoding(); + method = "compact unwind"; + } + if ( result == UNW_STEP_SUCCESS ) { + procinfo->logInfo ("Stepped via %s", method); + procinfo->timestamp_stop (step_remote, "stepRemote"); + if (frame_is_sigtramp) + fIsLeafFrame = true; + return result; + } + } + UnwindCursor::fRegisters = saved_registers; + } + + if (frame_is_sigtramp || frame_is_inferior_function_call_dummy) + fIsLeafFrame = true; // this will be true once we complete this stepRemote() + else + fIsLeafFrame = false; + + if (frame_is_inferior_function_call_dummy) { + if (stepOutOfDebuggerDummyFrame (UnwindCursor::fAddressSpace, UnwindCursor::fRegisters, procinfo, pc, sp, fArg) == UNW_STEP_SUCCESS) { + procinfo->logInfo ("Stepped via stepOutOfDebuggerDummyFrame"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return UNW_STEP_SUCCESS; + } + } + + // If we haven't already seen this function we'll need to get the function bounds via + // eh frame info (if available) - it's the most accurate function bounds in a + // stripped binary. After that we'll ask the driver program (via the get_proc_bounds accessor). + + if (procinfo->haveProfile (pc) == false) { + + uint64_t text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, mh; + uint64_t start_addr, end_addr; + if (pc == 0) { + int ret = stepByArchitectureDefault (UnwindCursor::fAddressSpace, UnwindCursor::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return ret; + } + + // If the address is not contained in any image's address range either we've walked off + // the stack into random memory or we're backtracing through jit'ed code on the heap. + // Let's assume the latter and follow the architecture's default stack walking scheme. + + if (!procinfo->getImageAddresses (pc, mh, text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, fArg)) { + int ret = stepByArchitectureDefault (UnwindCursor::fAddressSpace, UnwindCursor::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return ret; + } + if (procinfo->haveFuncBounds (mh) == false) { + struct timeval *get_func_bounds = procinfo->timestamp_start(); + std::vector func_bounds; + // CFI entries are usually around 38 bytes but under-estimate a bit + // because we're not distinguishing between CIEs and FDEs. + if (eh_frame_len > 0) + func_bounds.reserve (eh_frame_len / 16); + if (procinfo->getCachingPolicy() != UNW_CACHE_NONE) { + // cache the entire eh frame section - we'll need to read the whole + // thing anyway so we might as well save it. + uint8_t *eh_buf = (uint8_t *)malloc (eh_frame_len); + if (UnwindCursor::fAddressSpace.getBytes (eh_frame_start, eh_frame_len, eh_buf) == 0) + return UNW_EUNSPEC; + RemoteMemoryBlob *ehmem = new RemoteMemoryBlob(eh_buf, free, eh_frame_start, eh_frame_len, mh, NULL); + procinfo->addMemBlob (ehmem); + } + + if (CFI_Parser::functionFuncBoundsViaFDE(UnwindCursor::fAddressSpace, eh_frame_start, eh_frame_len, func_bounds)) { + procinfo->addFuncBounds(mh, func_bounds); + procinfo->logVerbose ("Added %d function bounds", (int) func_bounds.size()); + procinfo->timestamp_stop (get_func_bounds, "getting function bounds from EH frame FDEs"); + } + } + if (procinfo->findStartAddr (pc, start_addr, end_addr)) { + // If end_addr is 0, we might be looking at the final function in this binary image + if (start_addr != 0 && end_addr == 0) + end_addr = text_end; + procinfo->logVerbose ("Got function bounds from func bounds vector, 0x%llx-0x%llx", start_addr, end_addr); + } else { + if (UnwindCursor::fAddressSpace.accessors()->get_proc_bounds (procinfo->wrap(), pc, &start_addr, &end_addr, fArg) != UNW_ESUCCESS) { + int ret = stepByArchitectureDefault (UnwindCursor::fAddressSpace, UnwindCursor::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return ret; + } + else { + procinfo->logVerbose ("Got function bounds from get_proc_bounds callback, 0x%llx-0x%llx", start_addr, end_addr); + } + } + if (start_addr != 0) { + procinfo->addProfile (UnwindCursor::fAddressSpace.accessors(), UnwindCursor::fAddressSpace.wrap(), start_addr, end_addr, fArg); + } + } + + RemoteUnwindProfile *profile = procinfo->findProfile (pc); + if (profile == NULL) + return UNW_ENOINFO; + + int retval = stepWithAssembly (UnwindCursor::fAddressSpace, pc, profile, UnwindCursor::fRegisters); + if (retval >= 0) { + procinfo->logInfo ("Stepped via stepWithAssembly"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return retval; + } + + retval = stepByArchitectureDefault (UnwindCursor::fAddressSpace, UnwindCursor::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return retval; +} + +template +void RemoteUnwindCursor::setRemoteContext(void *arg) +{ + // fill in the register state for the currently executing frame. + getRemoteContext (UnwindCursor::fAddressSpace.getRemoteProcInfo(), UnwindCursor::fRegisters, arg); + + // Flag that this unwind cursor is pointing at the zeroth frame. We don't + // want to use compact unwind info / eh frame info to unwind out of this + // frame. + + fIsLeafFrame = true; + fIsFirstFrame = true; +} + +// This needs to be done in many of the functions and in libuwind.cxx in one or two +// places so I'm defining a convenience method. +template +bool RemoteUnwindCursor::caller_regno_to_unwind_regno (int caller_regno, int& unwind_regno) +{ + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + unwind_regno = caller_regno; + return true; + } + if (procinfo->getRegisterMap()->caller_regno_to_unwind_regno (caller_regno, unwind_regno)) + return true; + return false; +} + +template +int RemoteUnwindCursor::endOfPrologueInsns (unw_word_t start, unw_word_t end, unw_word_t *endofprologue) +{ + RemoteProcInfo *procinfo = UnwindCursor::fAddressSpace.getRemoteProcInfo(); + *endofprologue = start; + if (procinfo == NULL) { + ABORT("findEndOfPrologueSetup called with local unwind."); + return UNW_EUNSPEC; + } + if (procinfo->haveProfile (start) == false) { + uint64_t text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, mh; + if (!procinfo->getImageAddresses (start, mh, text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, fArg)) + return UNW_EUNSPEC; + if (end == 0) { + if (procinfo->haveFuncBounds (mh) == false) { + std::vector func_bounds; + // CFI entries are usually around 38 bytes but under-estimate a bit + // because we're not distinguishing between CIEs and FDEs. + if (eh_frame_len > 0) + func_bounds.reserve (eh_frame_len / 16); + if (procinfo->getCachingPolicy() != UNW_CACHE_NONE) { + // cache the entire eh frame section - we'll need to read the whole + // thing anyway so we might as well save it. + uint8_t *eh_buf = (uint8_t *)malloc (eh_frame_len); + if (UnwindCursor::fAddressSpace.getBytes (eh_frame_start, eh_frame_len, eh_buf) == 0) + return UNW_EUNSPEC; + RemoteMemoryBlob *ehmem = new RemoteMemoryBlob(eh_buf, free, eh_frame_start, eh_frame_len, mh, NULL); + procinfo->addMemBlob (ehmem); + } + if (CFI_Parser::functionFuncBoundsViaFDE(UnwindCursor::fAddressSpace, eh_frame_start, eh_frame_len, func_bounds)) { + procinfo->addFuncBounds(mh, func_bounds); + } + } + uint64_t bounded_start, bounded_end; + if (procinfo->findStartAddr (start, bounded_start, bounded_end)) { + end = bounded_end; + } else { + if (UnwindCursor::fAddressSpace.accessors()->get_proc_bounds (procinfo->wrap(), start, &bounded_start, &bounded_end, fArg) != UNW_ESUCCESS) + if (bounded_end != 0) + end = bounded_end; + } + } + if (procinfo->addProfile (UnwindCursor::fAddressSpace.accessors(), UnwindCursor::fAddressSpace.wrap(), start, end, fArg) == false) + return UNW_EUNSPEC; + } + RemoteUnwindProfile *profile = procinfo->findProfile (start); + if (profile == NULL) + return UNW_ENOINFO; + *endofprologue = profile->fFirstInsnPastPrologue; + return UNW_ESUCCESS; +} + +#endif // SUPPORT_REMOTE_UNWINDING + + +}; // namespace lldb_private + + +#endif // __UNWINDCURSOR_HPP__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c new file mode 100644 index 000000000000..7103c719ba29 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c @@ -0,0 +1,282 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- UnwindLevel1-gcc-ext.c ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * Implements gcc extensions to the C++ ABI Exception Handling Level 1 as documented at: + * + * using libunwind + * + */ + +#include +#include +#include +#include + +#include "libunwind.h" +#include "unwind.h" +#include "libunwind_priv.h" +#include "InternalMacros.h" + + +#if __ppc__ || __i386__ || __x86_64__ + +// +// Called by __cxa_rethrow() +// +EXPORT _Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n", exception_object, exception_object->private_1); + // if this is non-forced and a stopping place was found, then this is a re-throw + // call _Unwind_RaiseException() as if this was a new exception + if ( exception_object->private_1 == 0 ) + _Unwind_RaiseException(exception_object); + + // call through to _Unwind_Resume() which distiguishes between forced and regular exceptions + _Unwind_Resume(exception_object); + ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException() which unexpectedly returned"); +} + + + +// +// Called by personality handler during phase 2 to get base address for data relative encodings +// +EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) +{ + DEBUG_PRINT_API("_Unwind_GetDataRelBase(context=%p)\n", context); + ABORT("_Unwind_GetDataRelBase() not implemented"); +} + +// +// Called by personality handler during phase 2 to get base address for text relative encodings +// +EXPORT uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) +{ + DEBUG_PRINT_API("_Unwind_GetTextRelBase(context=%p)\n", context); + ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + + +// +// Scans unwind information to find the function that contains the +// specified code address "pc". +// +EXPORT void* _Unwind_FindEnclosingFunction(void* pc) +{ + DEBUG_PRINT_API("_Unwind_FindEnclosingFunction(pc=%p)\n", pc); + ABORT("_Unwind_FindEnclosingFunction() not implemented"); +} + + +// +// Walk every frame and call trace function at each one. If trace function +// returns anything other than _URC_NO_REASON, then walk is terminated. +// +EXPORT _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn callback, void* ref) +{ + unw_cursor_t cursor; + unw_context_t uc; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + + DEBUG_PRINT_API("_Unwind_Backtrace(callback=%p)\n", callback); + + // walk each frame + while ( true ) { + + // ask libuwind to get next frame (skip over first frame which is _Unwind_Backtrace()) + if ( unw_step(&cursor) <= 0 ) { + DEBUG_PRINT_UNWINDING(" _backtrace: ended because cursor reached bottom of stack, returning %d\n", _URC_END_OF_STACK); + return _URC_END_OF_STACK; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_proc_info_t frameInfo; + unw_word_t offset; + unw_get_proc_name(&cursor, functionName, 512, &offset); + unw_get_proc_info(&cursor, &frameInfo); + DEBUG_PRINT_UNWINDING(" _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n", + frameInfo.start_ip, functionName, frameInfo.lsda, &cursor); + } + + // call trace function with this frame + _Unwind_Reason_Code result = (*callback)((struct _Unwind_Context*)(&cursor), ref); + if ( result != _URC_NO_REASON ) { + DEBUG_PRINT_UNWINDING(" _backtrace: ended because callback returned %d\n", result); + return result; + } + } +} + + +// +// Find dwarf unwind info for an address 'pc' in some function. +// +EXPORT const void* _Unwind_Find_FDE(const void* pc, struct dwarf_eh_bases* bases) +{ + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long)pc); + unw_get_proc_info(&cursor, &info); + bases->tbase = info.extra; + bases->dbase = 0; // dbase not used on Mac OS X + bases->func = info.start_ip; + DEBUG_PRINT_API("_Unwind_Find_FDE(pc=%p) => %p\n", pc, (void*)(long)info.unwind_info); + return (void*)(long)info.unwind_info; +} + + + +EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_word_t result; + unw_get_reg(cursor, UNW_REG_SP, &result); + DEBUG_PRINT_API("_Unwind_GetCFA(context=%p) => 0x%llX\n", context, (uint64_t)result); + return result; +} + + +// +// Called by personality handler during phase 2 to get instruction pointer. +// ipBefore is a boolean that says if IP is already adjusted to be the call +// site address. Normally IP is the return address. +// +EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore) +{ + DEBUG_PRINT_API("_Unwind_GetIPInfo(context=%p)\n", context); + *ipBefore = 0; + return _Unwind_GetIP(context); +} + + +// +// Called by programs with dynamic code generators that want +// to register a dynamically generated FDE. +// This function has existed on Mac OS X since 10.4, but +// never worked before. +// +EXPORT void __register_frame(const void* fde) +{ + DEBUG_PRINT_API("__register_frame(%p)\n", fde); + _unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + +// +// Called by programs with dynamic code generators that want +// to unregister a dynamically generated FDE. +// This function has existed on Mac OS X since 10.4, but +// never worked before. +// +EXPORT void __deregister_frame(const void* fde) +{ + DEBUG_PRINT_API("__deregister_frame(%p)\n", fde); + _unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + + +// +// The following register/deregister functions are gcc extensions. +// They have existed on Mac OS X, but have never worked because Mac OS X +// before 10.6 used keymgr to track known FDEs, but these functions +// never got updated to use keymgr. +// For now, we implement these as do-nothing functions to keep any existing +// applications working. We also add the not in 10.6 symbol so that nwe +// application won't be able to use them. +// + +EXPORT void __register_frame_info_bases(const void* fde, void* ob, void* tb, void* db) +{ + DEBUG_PRINT_API("__register_frame_info_bases(%p,%p, %p, %p)\n", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void __register_frame_info(const void* fde, void* ob) +{ + DEBUG_PRINT_API("__register_frame_info(%p, %p)\n", fde, ob); + // do nothing, this function never worked in Mac OS X +} + + +EXPORT void __register_frame_info_table_bases(const void* fde, void* ob, void* tb, void* db) +{ + DEBUG_PRINT_API("__register_frame_info_table_bases(%p,%p, %p, %p)\n", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void __register_frame_info_table(const void* fde, void* ob) +{ + DEBUG_PRINT_API("__register_frame_info_table(%p, %p)\n", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void __register_frame_table(const void* fde) +{ + DEBUG_PRINT_API("__register_frame_table(%p)\n", fde); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void* __deregister_frame_info(const void* fde) +{ + DEBUG_PRINT_API("__deregister_frame_info(%p)\n", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + +EXPORT void* __deregister_frame_info_bases(const void* fde) +{ + DEBUG_PRINT_API("__deregister_frame_info_bases(%p)\n", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + + + + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_Backtrace) +NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction) +NOT_HERE_BEFORE_10_6(_Unwind_GetCFA) +NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow) +NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo) + +NOT_HERE_BEFORE_10_6(__register_frame) +NOT_HERE_BEFORE_10_6(__deregister_frame) + + +// +// symbols in libSystem.dylib for compatibility, but we don't want any new code using them +// +NEVER_HERE(__register_frame_info_bases) +NEVER_HERE(__register_frame_info) +NEVER_HERE(__register_frame_info_table_bases) +NEVER_HERE(__register_frame_info_table) +NEVER_HERE(__register_frame_table) +NEVER_HERE(__deregister_frame_info) +NEVER_HERE(__deregister_frame_info_bases) + + +#endif // __ppc__ || __i386__ || __x86_64__ + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c new file mode 100644 index 000000000000..3aa2b6f552c1 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c @@ -0,0 +1,443 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- UnwindLevel1.c ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * + * Implements C++ ABI Exception Handling Level 1 as documented at: + * + * using libunwind + * + */ + +#include +#include +#include +#include +#include + +#include "libunwind.h" +#include "unwind.h" +#include "InternalMacros.h" + +#if __ppc__ || __i386__ || __x86_64__ + +static _Unwind_Reason_Code unwind_phase1(unw_context_t* uc, struct _Unwind_Exception* exception_object) +{ + unw_cursor_t cursor1; + unw_init_local(&cursor1, uc); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; ) { + + // ask libuwind to get next frame (skip over first which is _Unwind_RaiseException) + int stepResult = unw_step(&cursor1); + if ( stepResult == 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + else if ( stepResult < 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // see if frame has code to run (has personality routine) + unw_proc_info_t frameInfo; + unw_word_t sp; + if ( unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_word_t offset; + if ( (unw_get_proc_name(&cursor1, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) ) + strcpy(functionName, ".anonymous."); + unw_word_t pc; + unw_get_reg(&cursor1, UNW_REG_IP, &pc); + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n", + exception_object, pc, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); + } + + // if there is a personality routine, ask it if it will want to stop at this frame + if ( frameInfo.handler != 0 ) { + __personality_routine p = (__personality_routine)(long)(frameInfo.handler); + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, p); + _Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor1)); + switch ( personalityResult ) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + handlerNotFound = false; + unw_get_reg(&cursor1, UNW_REG_SP, &sp); + exception_object->private_2 = sp; + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + // continue unwinding + break; + + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code unwind_phase2(unw_context_t* uc, struct _Unwind_Exception* exception_object) +{ + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + + // walk each frame until we reach where search phase said to stop + while ( true ) { + + // ask libuwind to get next frame (skip over first which is _Unwind_RaiseException) + int stepResult = unw_step(&cursor2); + if ( stepResult == 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + else if ( stepResult < 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // get info about this frame + unw_word_t sp; + unw_proc_info_t frameInfo; + unw_get_reg(&cursor2, UNW_REG_SP, &sp); + if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_word_t offset; + if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) ) + strcpy(functionName, ".anonymous."); + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, lsda=0x%llX, personality=0x%llX\n", + exception_object, frameInfo.start_ip, functionName, sp, frameInfo.lsda, frameInfo.handler); + } + + // if there is a personality routine, tell it we are unwinding + if ( frameInfo.handler != 0 ) { + __personality_routine p = (__personality_routine)(long)(frameInfo.handler); + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ( sp == exception_object->private_2 ) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); // tell personality this was the frame it marked in phase 1 + _Unwind_Reason_Code personalityResult = (*p)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor2)); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + if ( sp == exception_object->private_2 ) { + // phase 1 said we would stop at this frame, but we did not... + ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", exception_object); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + if ( DEBUG_PRINT_UNWINDING_TEST ) { + unw_word_t pc; + unw_word_t sp; + unw_get_reg(&cursor2, UNW_REG_IP, &pc); + unw_get_reg(&cursor2, UNW_REG_SP, &sp); + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering user code with ip=0x%llX, sp=0x%llX\n", exception_object, pc, sp); + } + unw_resume(&cursor2); + // unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t* uc, struct _Unwind_Exception* exception_object, + _Unwind_Stop_Fn stop, void* stop_parameter) +{ + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + // walk each frame until we reach where search phase said to stop + while ( unw_step(&cursor2) > 0 ) { + + // get info about this frame + unw_proc_info_t frameInfo; + if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) { + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step failed => _URC_END_OF_STACK\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_word_t offset; + if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) ) + strcpy(functionName, ".anonymous."); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n", + exception_object, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); + } + + // call stop function at each frame + _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = (*stop)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor2), stop_parameter); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult); + if ( stopResult != _URC_NO_REASON ) { + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if ( frameInfo.handler != 0 ) { + __personality_routine p = (__personality_routine)(long)(frameInfo.handler); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p); + _Unwind_Reason_Code personalityResult = (*p)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor2)); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + unw_resume(&cursor2); + break; + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n", + exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // call stop function one last time and tell it we've reached the end of the stack + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", exception_object); + _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE|_UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context*)(&cursor2), stop_parameter); + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +// +// Called by __cxa_throw. Only returns if there is a fatal error +// +EXPORT _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_RaiseException(ex_obj=%p)\n", exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, exception_object); + if ( phase1 != _URC_NO_REASON ) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, exception_object); +} + + +// +// When _Unwind_RaiseException() is in phase2, it hands control +// to the personality function at each frame. The personality +// may force a jump to a landing pad in that function, the landing +// pad code may then call _Unwind_Resume() to continue with the +// unwinding. Note: the call to _Unwind_Resume() is from compiler +// geneated user code. All other _Unwind_* routines are called +// by the C++ runtime __cxa_* routines. +// +// Re-throwing an exception is implemented by having the code call +// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +// +EXPORT void _Unwind_Resume(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_Resume(ex_obj=%p)\n", exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + if ( exception_object->private_1 != 0 ) + unwind_phase2_forced(&uc, exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2); + else + unwind_phase2(&uc, exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + ABORT("_Unwind_Resume() can't return"); +} + + + +// +// Not used by C++. +// Unwinds stack, calling "stop" function at each frame +// Could be used to implement longjmp(). +// +EXPORT _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter) +{ + DEBUG_PRINT_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n", exception_object, stop); + unw_context_t uc; + unw_getcontext(&uc); + + // mark that this is a forced unwind, so _Unwind_Resume() can do the right thing + exception_object->private_1 = (uintptr_t)stop; + exception_object->private_2 = (uintptr_t)stop_parameter; + + // doit + return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); +} + + +// +// Called by personality handler during phase 2 to get LSDA for current frame +// +EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS ) + result = frameInfo.lsda; + DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lX\n", context, result); + if ( result != 0 ) { + if ( *((uint8_t*)result) != 0xFF ) + DEBUG_MESSAGE("lsda at 0x%lX does not start with 0xFF\n", result); + } + return result; +} + + +// +// Called by personality handler during phase 2 to get register values +// +EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_word_t result; + unw_get_reg(cursor, index, &result); + DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%llX\n", context, index, (uint64_t)result); + return result; +} + + +// +// Called by personality handler during phase 2 to alter register values +// +EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0llX)\n", context, index, (uint64_t)new_value); + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_set_reg(cursor, index, new_value); +} + + +// +// Called by personality handler during phase 2 to get instruction pointer +// +EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_word_t result; + unw_get_reg(cursor, UNW_REG_IP, &result); + DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%llX\n", context, (uint64_t)result); + return result; +} + + +// +// Called by personality handler during phase 2 to alter instruction pointer +// +EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0llX)\n", context, (uint64_t)new_value); + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_set_reg(cursor, UNW_REG_IP, new_value); +} + + +// +// Called by personality handler during phase 2 to find the start of the function +// +EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS ) + result = frameInfo.start_ip; + DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p) => 0x%lX\n", context, result); + return result; +} + + +// +// Called by personality handler during phase 2 if a foreign exception is caught +// +EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object); + if ( exception_object->exception_cleanup != NULL ) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object); +} + + + + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_DeleteException) +NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE) +NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind) +NOT_HERE_BEFORE_10_6(_Unwind_GetGR) +NOT_HERE_BEFORE_10_6(_Unwind_GetIP) +NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_10_6(_Unwind_RaiseException) +NOT_HERE_BEFORE_10_6(_Unwind_Resume) +NOT_HERE_BEFORE_10_6(_Unwind_SetGR) +NOT_HERE_BEFORE_10_6(_Unwind_SetIP) + +#endif // __ppc__ || __i386__ || __x86_64__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h b/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h new file mode 100644 index 000000000000..83414332c5e4 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h @@ -0,0 +1,245 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- dwarf2.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. +*/ + + +#ifndef __DWARF2__ +#define __DWARF2__ + +namespace lldb_private { + +// dwarf unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by gcc compiler +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + +}; // namespace lldb_private + + +#endif + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h b/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h new file mode 100644 index 000000000000..fe25780c538f --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h @@ -0,0 +1,35 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- libunwind_priv.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND_PRIV__ +#define __LIBUNWIND_PRIV__ + +namespace lldb_private { +#include "libunwind.h" + +#ifdef __cplusplus +extern "C" { +#endif + // SPI + extern void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); + + // IPI + extern void _unw_add_dynamic_fde(unw_word_t fde); + extern void _unw_remove_dynamic_fde(unw_word_t fde); + +#ifdef __cplusplus +} +#endif + +}; // namespace lldb_private + + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx b/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx new file mode 100644 index 000000000000..e7e66a452f0e --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx @@ -0,0 +1,421 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- libuwind.cxx --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if __ppc__ || __i386__ || __x86_64__ + +#include +#include +#include + +#include "libunwind.h" +#include "libunwind_priv.h" + +#include "UnwindCursor.hpp" +#include "AddressSpace.hpp" + +#include "RemoteProcInfo.hpp" + +namespace lldb_private { + +// setup debug logging hooks +INITIALIZE_DEBUG_PRINT_API +INITIALIZE_DEBUG_PRINT_UNWINDING + +// internal object to represent this processes address space +static LocalAddressSpace sThisAddressSpace; + +#pragma mark Local API + +/// +/// record the registers and stack position of the caller +/// +extern int unw_getcontext(unw_context_t*); +// note: unw_getcontext() implemented in assembly + +/// +/// create a cursor of a thread in this process given 'context' recorded by unw_getcontext() +/// +EXPORT int unw_init_local(unw_cursor_t* cursor, unw_context_t* context) +{ + DEBUG_PRINT_API("unw_init_local(cursor=%p, context=%p)\n", cursor, context); + // use "placement new" to allocate UnwindCursor in the cursor buffer +#if __i386__ + new ((void*)cursor) UnwindCursor(context, sThisAddressSpace); +#elif __x86_64__ + new ((void*)cursor) UnwindCursor(context, sThisAddressSpace); +#elif __ppc__ + new ((void*)cursor) UnwindCursor(context, sThisAddressSpace); +#endif + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + co->setInfoBasedOnIPRegister(NULL); + + return UNW_ESUCCESS; +} + +/// +/// move cursor to next frame +/// +EXPORT int unw_step(unw_cursor_t* cursor) +{ + DEBUG_PRINT_API("unw_step(cursor=%p)\n", cursor); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->step(); +} + +/// +/// get value of specified register at cursor position in stack frame +/// +EXPORT int unw_get_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t* value) +{ + DEBUG_PRINT_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if (co->validReg(regNum) == 0) + return UNW_EBADREG; + return co->getReg(regNum, value); +} + +/// +/// get value of specified float register at cursor position in stack frame +/// +EXPORT int unw_get_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t* value) +{ + DEBUG_PRINT_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if ( co->validFloatReg(regNum) ) { + return co->getFloatReg(regNum, value); + } + return UNW_EBADREG; +} + +/// +/// set value of specified register at cursor position in stack frame +/// +EXPORT int unw_set_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t value) +{ + DEBUG_PRINT_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if ( co->validReg(regNum) ) { + co->setReg(regNum, value); + // specical case altering IP to re-find info (being called by personality function) + if ( regNum == UNW_REG_IP ) { + unw_proc_info_t info; + co->getInfo(&info); + uint64_t orgArgSize = info.gp; + uint64_t orgFuncStart = info.start_ip; + co->setInfoBasedOnIPRegister(false); + // and adjust REG_SP if there was a DW_CFA_GNU_args_size + if ( (orgFuncStart == info.start_ip) && (orgArgSize != 0) ) + co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize); + } + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} + +/// +/// set value of specified float register at cursor position in stack frame +/// +EXPORT int unw_set_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t value) +{ + DEBUG_PRINT_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if ( co->validFloatReg(regNum) ) { + return co->setFloatReg(regNum, value); + } + return UNW_EBADREG; +} + +/// +/// resume execution at cursor position (aka longjump) +/// +EXPORT int unw_resume(unw_cursor_t* cursor) +{ + DEBUG_PRINT_API("unw_resume(cursor=%p)\n", cursor); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + co->jumpto(); + return UNW_EUNSPEC; +} + +/// +/// returns the name of a register +/// +EXPORT const char* unw_regname(unw_cursor_t* cursor, unw_regnum_t regNum) +{ + DEBUG_PRINT_API("unw_regname(cursor=%p, regNum=%d)\n", cursor, regNum); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->getRegisterName(regNum); +} + +/// +/// get unwind info at cursor position in stack frame +/// +EXPORT int unw_get_proc_info(unw_cursor_t* cursor, unw_proc_info_t* info) +{ + DEBUG_PRINT_API("unw_get_proc_info(cursor=%p, &info=%p)\n", cursor, info); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + co->getInfo(info); + if ( info->end_ip == 0 ) + return UNW_ENOINFO; + else + return UNW_ESUCCESS; +} + +/// +/// checks if a register is a floating-point register +/// +EXPORT int unw_is_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum) +{ + DEBUG_PRINT_API("unw_is_fpreg(cursor=%p, regNum=%d)\n", cursor, regNum); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->validFloatReg(regNum); +} + +/// +/// checks if current frame is signal trampoline +/// +EXPORT int unw_is_signal_frame(unw_cursor_t* cursor) +{ + DEBUG_PRINT_API("unw_is_signal_frame(cursor=%p)\n", cursor); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->isSignalFrame(); +} + +/// +/// get name of function at cursor position in stack frame +/// +EXPORT int unw_get_proc_name(unw_cursor_t* cursor, char* buf, size_t bufLen, unw_word_t* offset) +{ + DEBUG_PRINT_API("unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%ld)\n", cursor, buf, bufLen); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + if ( co->getFunctionName(buf, bufLen, offset) ) + return UNW_ESUCCESS; + else + return UNW_EUNSPEC; +} + +#pragma mark Remote API + +#if defined (SUPPORT_REMOTE_UNWINDING) +EXPORT int unw_init_remote(unw_cursor_t *cursor, unw_addr_space_t as, void *arg) +{ + DEBUG_PRINT_API("init_remote(c=%p, as=%p, arg=%p)\n", cursor, as, arg); + + // API docs at http://www.nongnu.org/libunwind/docs.html say we should + // handle a local address space but we're not doing the "remote" unwinding + // with local process accessors so punt on that. + + if(as->type != UNW_REMOTE) + { + ABORT("unw_init_remote was passed a non-remote address space"); + return UNW_EINVAL; + } + + unw_accessors_t* acc = unw_get_accessors(as); + if(!acc) { + ABORT("unw_get_accessors returned NULL"); + return UNW_EINVAL; + } + + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + + // use "placement new" to allocate UnwindCursor in the cursor buffer + // It isn't really necessary to use placement new in the remote API but we'll stay consistent + // with the rest of the code here. + switch ( remote->ras->getTargetArch() ) { + case UNW_TARGET_I386: + { + Registers_x86 *r = new Registers_x86; + OtherAddressSpace > *addrSpace = new OtherAddressSpace >(as, arg); + getRemoteContext (remote->ras, *r, arg); + unw_context_t *context = (unw_context_t*) r; + new ((void*)cursor) RemoteUnwindCursor >, Registers_x86>(*addrSpace, context, arg); + break; + } + break; + case UNW_TARGET_X86_64: + { + Registers_x86_64 *r = new Registers_x86_64; + OtherAddressSpace > *addrSpace = new OtherAddressSpace >(as, arg); + getRemoteContext (remote->ras, *r, arg); + unw_context_t *context = (unw_context_t*) r; + new ((void*)cursor) RemoteUnwindCursor >, Registers_x86_64>(*addrSpace, context, arg); + break; + } + + case UNW_TARGET_PPC: + ABORT("ppc not supported for remote unwinds"); + break; + + case UNW_TARGET_ARM: + ABORT("arm not supported for remote unwinds"); + break; + + default: + return UNW_EUNSPEC; + } + + AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor; + co->setRemoteContext(arg); + + return UNW_ESUCCESS; +} + +// The documentation disagrees about whether or not this returns a pointer. Now it does. +EXPORT unw_accessors_t* unw_get_accessors(unw_addr_space_t as) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_get_accessors was passed a non-remote address space"); + return NULL; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + + if(remote->type != UNW_REMOTE) + return NULL; + + return remote->ras->getAccessors(); +} + +EXPORT unw_addr_space_t unw_create_addr_space(unw_accessors_t *ap, unw_targettype_t targarch) +{ + unw_addr_space_remote* remote = (unw_addr_space_remote*)malloc(sizeof(unw_addr_space_remote)); + remote->type = UNW_REMOTE; + remote->ras = new RemoteProcInfo(ap, targarch); + return (unw_addr_space_t)remote; +} + +EXPORT void unw_flush_caches(unw_addr_space_t as) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_flush_caches was passed a non-remote address space"); + return; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + remote->ras->flushAllCaches(); + + return; +} + +EXPORT void unw_image_was_unloaded (unw_addr_space_t as, unw_word_t mh) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_image_was_unloaded was passed a non-remote address space"); + return; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + remote->ras->flushCacheByMachHeader(mh); + + return; +} + + +EXPORT int unw_set_caching_policy(unw_addr_space_t as, unw_caching_policy_t policy) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_set_caching_policy was passed a non-remote address space"); + return UNW_EINVAL; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + return remote->ras->setCachingPolicy(policy); +} + +EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&sThisAddressSpace; + +/// +/// delete an address_space object +/// +EXPORT void unw_destroy_addr_space(unw_addr_space_t asp) +{ + if(asp->type != UNW_REMOTE) { + ABORT("unw_destroy_addr_space was passed a non-remote address space"); + return; + } + + unw_addr_space_remote* remote = (unw_addr_space_remote*)asp; + delete remote->ras; +} + +EXPORT void unw_set_logging_level(unw_addr_space_t as, FILE *f, unw_log_level_t level) +{ + if (as->type != UNW_REMOTE) { + ABORT("unw_set_logging_level was passed a non-remote address space"); + return; + } + + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + return remote->ras->setLoggingLevel(f, level); +} + + +EXPORT int unw_end_of_prologue_setup(unw_cursor_t* cursor, unw_word_t start, unw_word_t end, unw_word_t *endofprologue) +{ + AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor; + if (!co->remoteUnwindCursor()) + ABORT("unw_end_of_prologue_setup called with a non-remote unwind cursor."); + + return co->endOfPrologueInsns (start, end, endofprologue); +} + + +#endif // SUPPORT_REMOTE_UNWINDING + +#pragma mark Dynamic unwinding API + +#if !FOR_DYLD +/// +/// SPI: walks cached dwarf entries +/// +EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) +{ + DEBUG_PRINT_API("unw_iterate_dwarf_unwind_cache(func=%p)\n", func); + DwarfFDECache::iterateCacheEntries(func); +} +#endif // !FOR_DYLD + +#if !FOR_DYLD +// +// IPI: for __register_frame() +// +void _unw_add_dynamic_fde(unw_word_t fde) +{ + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char* message = CFI_Parser::decodeFDE(sThisAddressSpace, (LocalAddressSpace::pint_t)fde, & fdeInfo, &cieInfo); + if ( message == NULL ) { + // dynamically registered FDEs don't have a mach_header group they are in. Use fde as mh_group + unw_word_t mh_group = fdeInfo.fdeStart; + DwarfFDECache::add(mh_group, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart); + } + else { + DEBUG_MESSAGE("_unw_add_dynamic_fde: bad fde: %s", message); + } +} + +// +// IPI: for __deregister_frame() +// +void _unw_remove_dynamic_fde(unw_word_t fde) +{ + // fde is own mh_group + DwarfFDECache::removeAllIn(fde); +} +#endif + +}; // namespace lldb_private + +#endif // __ppc__ || __i386__ || __x86_64__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s b/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s new file mode 100644 index 000000000000..8d3a451fd924 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s @@ -0,0 +1,229 @@ + +#if __i386__ || __x86_64__ || __ppc__ + + .text + .globl _unw_getcontext +_unw_getcontext: + +#endif // __i386__ || __x86_64__ || __ppc__ + + +#if __i386__ + +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + +# + push %eax + movl 8(%esp), %eax + movl %ebx, 4(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) + movl %edi, 16(%eax) + movl %esi, 20(%eax) + movl %ebp, 24(%eax) + movl %esp, %edx + addl $8, %edx + movl %edx, 28(%eax) # store what sp was at call site as esp + # skip ss + # skip eflags + movl 4(%esp), %edx + movl %edx, 40(%eax) # store return address as eip + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + movl (%esp), %edx + movl %edx, (%eax) # store original eax + popl %eax + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif __x86_64__ + +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in rdi +# + movq %rax, (%rdi) + movq %rbx, 8(%rdi) + movq %rcx, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rdi, 32(%rdi) + movq %rsi, 40(%rdi) + movq %rbp, 48(%rdi) + movq %rsp, 56(%rdi) + addq $8, 56(%rdi) + movq %r8, 64(%rdi) + movq %r9, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13,104(%rdi) + movq %r14,112(%rdi) + movq %r15,120(%rdi) + movq (%rsp),%rsi + movq %rsi,128(%rdi) # store return address as rip + # skip rflags + # skip cs + # skip fs + # skip gs + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif __ppc__ + +; +; extern int unw_getcontext(unw_context_t* thread_state) +; +; On entry: +; thread_state pointer is in r3 +; + stw r0, 8(r3) + mflr r0 + stw r0, 0(r3) ; store lr as ssr0 + stw r1, 12(r3) + stw r2, 16(r3) + stw r3, 20(r3) + stw r4, 24(r3) + stw r5, 28(r3) + stw r6, 32(r3) + stw r7, 36(r3) + stw r8, 40(r3) + stw r9, 44(r3) + stw r10, 48(r3) + stw r11, 52(r3) + stw r12, 56(r3) + stw r13, 60(r3) + stw r14, 64(r3) + stw r15, 68(r3) + stw r16, 72(r3) + stw r17, 76(r3) + stw r18, 80(r3) + stw r19, 84(r3) + stw r20, 88(r3) + stw r21, 92(r3) + stw r22, 96(r3) + stw r23,100(r3) + stw r24,104(r3) + stw r25,108(r3) + stw r26,112(r3) + stw r27,116(r3) + stw r28,120(r3) + stw r29,124(r3) + stw r30,128(r3) + stw r31,132(r3) + + ; save VRSave register + mfspr r0,256 + stw r0,156(r3) + ; save CR registers + mfcr r0 + stw r0,136(r3) + ; save CTR register + mfctr r0 + stw r0,148(r3) + + ; save float registers + stfd f0, 160(r3) + stfd f1, 168(r3) + stfd f2, 176(r3) + stfd f3, 184(r3) + stfd f4, 192(r3) + stfd f5, 200(r3) + stfd f6, 208(r3) + stfd f7, 216(r3) + stfd f8, 224(r3) + stfd f9, 232(r3) + stfd f10,240(r3) + stfd f11,248(r3) + stfd f12,256(r3) + stfd f13,264(r3) + stfd f14,272(r3) + stfd f15,280(r3) + stfd f16,288(r3) + stfd f17,296(r3) + stfd f18,304(r3) + stfd f19,312(r3) + stfd f20,320(r3) + stfd f21,328(r3) + stfd f22,336(r3) + stfd f23,344(r3) + stfd f24,352(r3) + stfd f25,360(r3) + stfd f26,368(r3) + stfd f27,376(r3) + stfd f28,384(r3) + stfd f29,392(r3) + stfd f30,400(r3) + stfd f31,408(r3) + + + ; save vector registers + + subi r4,r1,16 + rlwinm r4,r4,0,0,27 ; mask low 4-bits + ; r4 is now a 16-byte aligned pointer into the red zone + +#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \ + stvx _vec,0,r4 @\ + lwz r5, 0(r4) @\ + stw r5, _offset(r3) @\ + lwz r5, 4(r4) @\ + stw r5, _offset+4(r3) @\ + lwz r5, 8(r4) @\ + stw r5, _offset+8(r3) @\ + lwz r5, 12(r4) @\ + stw r5, _offset+12(r3) + + SAVE_VECTOR_UNALIGNED( v0, 424+0x000) + SAVE_VECTOR_UNALIGNED( v1, 424+0x010) + SAVE_VECTOR_UNALIGNED( v2, 424+0x020) + SAVE_VECTOR_UNALIGNED( v3, 424+0x030) + SAVE_VECTOR_UNALIGNED( v4, 424+0x040) + SAVE_VECTOR_UNALIGNED( v5, 424+0x050) + SAVE_VECTOR_UNALIGNED( v6, 424+0x060) + SAVE_VECTOR_UNALIGNED( v7, 424+0x070) + SAVE_VECTOR_UNALIGNED( v8, 424+0x080) + SAVE_VECTOR_UNALIGNED( v9, 424+0x090) + SAVE_VECTOR_UNALIGNED(v10, 424+0x0A0) + SAVE_VECTOR_UNALIGNED(v11, 424+0x0B0) + SAVE_VECTOR_UNALIGNED(v12, 424+0x0C0) + SAVE_VECTOR_UNALIGNED(v13, 424+0x0D0) + SAVE_VECTOR_UNALIGNED(v14, 424+0x0E0) + SAVE_VECTOR_UNALIGNED(v15, 424+0x0F0) + SAVE_VECTOR_UNALIGNED(v16, 424+0x100) + SAVE_VECTOR_UNALIGNED(v17, 424+0x110) + SAVE_VECTOR_UNALIGNED(v18, 424+0x120) + SAVE_VECTOR_UNALIGNED(v19, 424+0x130) + SAVE_VECTOR_UNALIGNED(v20, 424+0x140) + SAVE_VECTOR_UNALIGNED(v21, 424+0x150) + SAVE_VECTOR_UNALIGNED(v22, 424+0x160) + SAVE_VECTOR_UNALIGNED(v23, 424+0x170) + SAVE_VECTOR_UNALIGNED(v24, 424+0x180) + SAVE_VECTOR_UNALIGNED(v25, 424+0x190) + SAVE_VECTOR_UNALIGNED(v26, 424+0x1A0) + SAVE_VECTOR_UNALIGNED(v27, 424+0x1B0) + SAVE_VECTOR_UNALIGNED(v28, 424+0x1C0) + SAVE_VECTOR_UNALIGNED(v29, 424+0x1D0) + SAVE_VECTOR_UNALIGNED(v30, 424+0x1E0) + SAVE_VECTOR_UNALIGNED(v31, 424+0x1F0) + + li r3, 0 ; return UNW_ESUCCESS + blr + + + +#endif + diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp new file mode 100644 index 000000000000..cac2101e4c05 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -0,0 +1,813 @@ +//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "GDBRemoteCommunication.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Args.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/TimeValue.h" + +// Project includes +#include "StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteCommunication constructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::GDBRemoteCommunication() : + Communication("gdb-remote.packets"), + m_send_acks (true), + m_rx_packet_listener ("gdbremote.rx_packet"), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_is_running (false), + m_async_mutex (Mutex::eMutexTypeRecursive), + m_async_packet_predicate (false), + m_async_packet (), + m_async_response (), + m_async_timeout (UINT32_MAX), + m_async_signal (-1), + m_arch(), + m_os(), + m_vendor(), + m_byte_order(eByteOrderHost), + m_pointer_byte_size(0) +{ + m_rx_packet_listener.StartListeningForEvents(this, + Communication::eBroadcastBitPacketAvailable | + Communication::eBroadcastBitReadThreadDidExit); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::~GDBRemoteCommunication() +{ + m_rx_packet_listener.StopListeningForEvents(this, + Communication::eBroadcastBitPacketAvailable | + Communication::eBroadcastBitReadThreadDidExit); + if (IsConnected()) + { + StopReadThread(); + Disconnect(); + } +} + + +char +GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length) +{ + int checksum = 0; + + // We only need to compute the checksum if we are sending acks + if (m_send_acks) + { + for (int i = 0; i < payload_length; ++i) + checksum += payload[i]; + } + return checksum & 255; +} + +size_t +GDBRemoteCommunication::SendAck (char ack_char) +{ + Mutex::Locker locker(m_sequence_mutex); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %c", ack_char); + ConnectionStatus status = eConnectionStatusSuccess; + return Write (&ack_char, 1, status, NULL) == 1; +} + +size_t +GDBRemoteCommunication::SendPacketAndWaitForResponse +( + const char *payload, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async +) +{ + return SendPacketAndWaitForResponse (payload, + ::strlen (payload), + response, + timeout_seconds, + send_async); +} + +size_t +GDBRemoteCommunication::SendPacketAndWaitForResponse +( + const char *payload, + size_t payload_length, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async +) +{ + Mutex::Locker locker; + TimeValue timeout_time; + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds (timeout_seconds); + + if (locker.TryLock (m_sequence_mutex.GetMutex())) + { + if (SendPacketNoLock (payload, strlen(payload))) + return WaitForPacketNoLock (response, &timeout_time); + } + else + { + if (send_async) + { + Mutex::Locker async_locker (m_async_mutex); + m_async_packet.assign(payload, payload_length); + m_async_timeout = timeout_seconds; + m_async_packet_predicate.SetValue (true, eBroadcastNever); + + bool timed_out = false; + if (SendInterrupt(1, &timed_out)) + { + if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out)) + { + response = m_async_response; + return response.GetStringRef().size(); + } + } +// if (timed_out) +// m_error.SetErrorString("Timeout."); +// else +// m_error.SetErrorString("Unknown error."); + } + else + { +// m_error.SetErrorString("Sequence mutex is locked."); + } + } + return 0; +} + +//template +//class ScopedValueChanger +//{ +//public: +// // Take a value reference and the value to assing it to when this class +// // instance goes out of scope. +// ScopedValueChanger (_Tp &value_ref, _Tp value) : +// m_value_ref (value_ref), +// m_value (value) +// { +// } +// +// // This object is going out of scope, change the value pointed to by +// // m_value_ref to the value we got during construction which was stored in +// // m_value; +// ~ScopedValueChanger () +// { +// m_value_ref = m_value; +// } +//protected: +// _Tp &m_value_ref; // A reference to the value we wil change when this object destructs +// _Tp m_value; // The value to assign to m_value_ref when this goes out of scope. +//}; + +StateType +GDBRemoteCommunication::SendContinuePacketAndWaitForResponse +( + ProcessGDBRemote *process, + const char *payload, + size_t packet_length, + StringExtractorGDBRemote &response +) +{ + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (log) + log->Printf ("GDBRemoteCommunication::%s ()", __FUNCTION__); + + Mutex::Locker locker(m_sequence_mutex); + m_is_running.SetValue (true, eBroadcastNever); + +// ScopedValueChanger restore_running_to_false (m_is_running, false); + StateType state = eStateRunning; + + if (SendPacket(payload, packet_length) == 0) + state = eStateInvalid; + + while (state == eStateRunning) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...)", __FUNCTION__); + + if (WaitForPacket (response, (TimeValue*)NULL)) + { + if (response.Empty()) + state = eStateInvalid; + else + { + const char stop_type = response.GetChar(); + if (log) + log->Printf ("GDBRemoteCommunication::%s () got '%c' packet", __FUNCTION__, stop_type); + switch (stop_type) + { + case 'T': + case 'S': + if (m_async_signal != -1) + { + // Save off the async signal we are supposed to send + const int async_signal = m_async_signal; + // Clear the async signal member so we don't end up + // sending the signal multiple times... + m_async_signal = -1; + // Check which signal we stopped with + uint8_t signo = response.GetHexU8(255); + if (signo == async_signal) + { + // We already stopped with a signal that we wanted + // to stop with, so we are done + response.SetFilePos (0); + } + else + { + // We stopped with a different signal that the one + // we wanted to stop with, so now we must resume + // with the signal we want + char signal_packet[32]; + int signal_packet_len = 0; + signal_packet_len = ::snprintf (signal_packet, + sizeof (signal_packet), + "C%2.2x", + async_signal); + + if (SendPacket(signal_packet, signal_packet_len) == 0) + { + state = eStateInvalid; + break; + } + else + continue; + } + } + else if (m_async_packet_predicate.GetValue()) + { + // We are supposed to send an asynchronous packet while + // we are running. + m_async_response.Clear(); + if (!m_async_packet.empty()) + { + SendPacketAndWaitForResponse (m_async_packet.data(), + m_async_packet.size(), + m_async_response, + m_async_timeout, + false); + } + // Let the other thread that was trying to send the async + // packet know that the packet has been sent. + m_async_packet_predicate.SetValue(false, eBroadcastAlways); + + // Continue again + if (SendPacket("c", 1) == 0) + { + state = eStateInvalid; + break; + } + else + continue; + } + // Stop with signal and thread info + state = eStateStopped; + break; + + case 'W': + // process exited + state = eStateExited; + break; + + case 'O': + // STDOUT + { + std::string inferior_stdout; + inferior_stdout.reserve(response.GetBytesLeft () / 2); + char ch; + while ((ch = response.GetHexU8()) != '\0') + inferior_stdout.append(1, ch); + process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size()); + } + break; + + case 'E': + // ERROR + state = eStateInvalid; + break; + + default: + if (log) + log->Printf ("GDBRemoteCommunication::%s () got unrecognized async packet: '%s'", __FUNCTION__, stop_type); + break; + } + } + } + else + { + if (log) + log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...) => false", __FUNCTION__); + state = eStateInvalid; + } + } + if (log) + log->Printf ("GDBRemoteCommunication::%s () => %s", __FUNCTION__, StateAsCString(state)); + response.SetFilePos(0); + m_is_running.SetValue (false, eBroadcastOnChange); + return state; +} + +size_t +GDBRemoteCommunication::SendPacket (const char *payload) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, ::strlen (payload)); +} + +size_t +GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, payload_length); +} + +size_t +GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length) +{ + if (IsConnected()) + { + StreamString packet(0, 4, eByteOrderBig); + + packet.PutChar('$'); + packet.Write (payload, payload_length); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum (payload, payload_length)); + + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %s", packet.GetData()); + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); + if (bytes_written == packet.GetSize()) + { + if (m_send_acks) + GetAck (1) == '+'; + } + return bytes_written; + } + //m_error.SetErrorString("Not connected."); + return 0; +} + +char +GDBRemoteCommunication::GetAck (uint32_t timeout_seconds) +{ + StringExtractorGDBRemote response; + if (WaitForPacket (response, timeout_seconds) == 1) + return response.GetChar(); + return 0; +} + +bool +GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex.GetMutex()); +} + +bool +GDBRemoteCommunication::SendAsyncSignal (int signo) +{ + m_async_signal = signo; + bool timed_out = false; + if (SendInterrupt(1, &timed_out)) + return true; + m_async_signal = -1; + return false; +} + +bool +GDBRemoteCommunication::SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out) +{ + if (timed_out) + *timed_out = false; + + if (IsConnected() && IsRunning()) + { + // Only send an interrupt if our debugserver is running... + if (m_sequence_mutex.TryLock() != 0) + { + // Someone has the mutex locked waiting for a response or for the + // inferior to stop, so send the interrupt on the down low... + char ctrl_c = '\x03'; + ConnectionStatus status = eConnectionStatusSuccess; + TimeValue timeout; + if (seconds_to_wait_for_stop) + { + timeout = TimeValue::Now(); + timeout.OffsetWithSeconds (seconds_to_wait_for_stop); + } + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: \\x03"); + if (Write (&ctrl_c, 1, status, NULL) > 0) + { + if (seconds_to_wait_for_stop) + m_is_running.WaitForValueEqualTo (false, &timeout, timed_out); + return true; + } + } + } + return false; +} + +size_t +GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, uint32_t timeout_seconds) +{ + TimeValue timeout_time; + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds (timeout_seconds); + return WaitForPacketNoLock (response, &timeout_time); +} + +size_t +GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketNoLock (response, timeout_time_ptr); +} + +size_t +GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr) +{ + bool checksum_error = false; + response.Clear (); + + EventSP event_sp; + + if (m_rx_packet_listener.WaitForEvent (timeout_time_ptr, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_type | Communication::eBroadcastBitPacketAvailable) + { + const EventDataBytes *event_bytes = EventDataBytes::GetEventDataFromEvent(event_sp.get()); + if (event_bytes) + { + const char * packet_data = (const char *)event_bytes->GetBytes(); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "read packet: %s", packet_data); + const size_t packet_size = event_bytes->GetByteSize(); + if (packet_data && packet_size > 0) + { + std::string &response_str = response.GetStringRef(); + if (packet_data[0] == '$') + { + assert (packet_size >= 4); // Must have at least '$#CC' where CC is checksum + assert (packet_data[packet_size-3] == '#'); + assert (::isxdigit (packet_data[packet_size-2])); // Must be checksum hex byte + assert (::isxdigit (packet_data[packet_size-1])); // Must be checksum hex byte + response_str.assign (packet_data + 1, packet_size - 4); + if (m_send_acks) + { + char packet_checksum = strtol (&packet_data[packet_size-2], NULL, 16); + char actual_checksum = CalculcateChecksum (response_str.data(), response_str.size()); + checksum_error = packet_checksum != actual_checksum; + // Send the ack or nack if needed + if (checksum_error) + SendAck('-'); + else + SendAck('+'); + } + } + else + { + response_str.assign (packet_data, packet_size); + } + return response_str.size(); + } + } + } + else if (Communication::eBroadcastBitReadThreadDidExit) + { + // Our read thread exited on us so just fall through and return zero... + } + } + return 0; +} + +void +GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + m_bytes.append ((const char *)src, src_len); + + // Parse up the packets into gdb remote packets + while (!m_bytes.empty()) + { + // end_idx must be one past the last valid packet byte. Start + // it off with an invalid value that is the same as the current + // index. + size_t end_idx = 0; + + switch (m_bytes[0]) + { + case '+': // Look for ack + case '-': // Look for cancel + case '\x03': // ^C to halt target + end_idx = 1; // The command is one byte long... + break; + + case '$': + // Look for a standard gdb packet? + end_idx = m_bytes.find('#'); + if (end_idx != std::string::npos) + { + if (end_idx + 2 < m_bytes.size()) + { + end_idx += 3; + } + else + { + // Checksum bytes aren't all here yet + end_idx = std::string::npos; + } + } + break; + + default: + break; + } + + if (end_idx == std::string::npos) + { + //ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE, "GDBRemoteCommunication::%s packet not yet complete: '%s'",__FUNCTION__, m_bytes.c_str()); + return; + } + else if (end_idx > 0) + { + // We have a valid packet... + assert (end_idx <= m_bytes.size()); + std::auto_ptr event_bytes_ap (new EventDataBytes (&m_bytes[0], end_idx)); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "got full packet: %s", event_bytes_ap->GetBytes()); + BroadcastEvent (eBroadcastBitPacketAvailable, event_bytes_ap.release()); + m_bytes.erase(0, end_idx); + } + else + { + assert (1 <= m_bytes.size()); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "GDBRemoteCommunication::%s tossing junk byte at %c",__FUNCTION__, m_bytes[0]); + m_bytes.erase(0, 1); + } + } +} + +lldb::pid_t +GDBRemoteCommunication::GetCurrentProcessID (uint32_t timeout_seconds) +{ + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, timeout_seconds, false)) + { + if (response.GetChar() == 'Q') + if (response.GetChar() == 'C') + return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); + } + return LLDB_INVALID_PROCESS_ID; +} + +bool +GDBRemoteCommunication::GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str) +{ + error_str.clear(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, timeout_seconds, false)) + { + if (response.IsOKPacket()) + return true; + if (response.GetChar() == 'E') + { + // A string the describes what failed when launching... + error_str = response.GetStringRef().substr(1); + } + else + { + error_str.assign ("unknown error occurred launching process"); + } + } + else + { + error_str.assign ("failed to send the qLaunchSuccess packet"); + } + return false; +} + +int +GDBRemoteCommunication::SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds) +{ + if (argv && argv[0]) + { + StreamString packet; + packet.PutChar('A'); + const char *arg; + for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i) + { + const int arg_len = strlen(arg); + if (i > 0) + packet.PutChar(','); + packet.Printf("%i,%i,", arg_len * 2, i); + packet.PutBytesAsRawHex8(arg, arg_len, eByteOrderHost, eByteOrderHost); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false)) + { + if (response.IsOKPacket()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunication::SendEnvironmentPacket (char const *name_equal_value, uint32_t timeout_seconds) +{ + if (name_equal_value && name_equal_value[0]) + { + StreamString packet; + packet.Printf("QEnvironment:%s", name_equal_value); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false)) + { + if (response.IsOKPacket()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +bool +GDBRemoteCommunication::GetHostInfo (uint32_t timeout_seconds) +{ + m_arch.Clear(); + m_os.Clear(); + m_vendor.Clear(); + m_byte_order = eByteOrderHost; + m_pointer_byte_size = 0; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse ("qHostInfo", response, timeout_seconds, false)) + { + if (response.IsUnsupportedPacket()) + return false; + + + std::string name; + std::string value; + while (response.GetNameColonValue(name, value)) + { + if (name.compare("cputype") == 0) + { + // exception type in big endian hex + m_arch.SetCPUType(Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0)); + } + else if (name.compare("cpusubtype") == 0) + { + // exception count in big endian hex + m_arch.SetCPUSubtype(Args::StringToUInt32 (value.c_str(), 0, 0)); + } + else if (name.compare("ostype") == 0) + { + // exception data in big endian hex + m_os.SetCString(value.c_str()); + } + else if (name.compare("vendor") == 0) + { + m_vendor.SetCString(value.c_str()); + } + else if (name.compare("endian") == 0) + { + if (value.compare("little") == 0) + m_byte_order = eByteOrderLittle; + else if (value.compare("big") == 0) + m_byte_order = eByteOrderBig; + else if (value.compare("pdp") == 0) + m_byte_order = eByteOrderPDP; + } + else if (name.compare("ptrsize") == 0) + { + m_pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0); + } + } + } + return HostInfoIsValid(); +} + +int +GDBRemoteCommunication::SendAttach +( + lldb::pid_t pid, + uint32_t timeout_seconds, + StringExtractorGDBRemote& response +) +{ + if (pid != LLDB_INVALID_PROCESS_ID) + { + StreamString packet; + packet.Printf("vAttach;%x", pid); + + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false)) + { + if (response.IsErrorPacket()) + return response.GetError(); + return 0; + } + } + return -1; +} + +const lldb_private::ArchSpec & +GDBRemoteCommunication::GetHostArchitecture () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_arch; +} + +const lldb_private::ConstString & +GDBRemoteCommunication::GetOSString () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_os; +} + +const lldb_private::ConstString & +GDBRemoteCommunication::GetVendorString() +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_vendor; +} + +lldb::ByteOrder +GDBRemoteCommunication::GetByteOrder () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_byte_order; +} + +uint32_t +GDBRemoteCommunication::GetAddressByteSize () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_pointer_byte_size; +} + +addr_t +GDBRemoteCommunication::AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds) +{ + char packet[64]; + ::snprintf (packet, sizeof(packet), "_M%zx,%s%s%s", size, + permissions & lldb::ePermissionsReadable ? "r" : "", + permissions & lldb::ePermissionsWritable ? "w" : "", + permissions & lldb::ePermissionsExecutable ? "x" : ""); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false)) + { + if (!response.IsErrorPacket()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } + return LLDB_INVALID_ADDRESS; +} + +bool +GDBRemoteCommunication::DeallocateMemory (addr_t addr, uint32_t timeout_seconds) +{ + char packet[64]; + snprintf(packet, sizeof(packet), "_m%llx", (uint64_t)addr); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false)) + { + if (!response.IsOKPacket()) + return true; + } + return false; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h new file mode 100644 index 000000000000..051fa445ff16 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -0,0 +1,270 @@ +//===-- GDBRemoteCommunication.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunication_h_ +#define liblldb_GDBRemoteCommunication_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Listener.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" + +#include "StringExtractorGDBRemote.h" + +class ProcessGDBRemote; + +class GDBRemoteCommunication : + public lldb_private::Communication +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteCommunication(); + + virtual + ~GDBRemoteCommunication(); + + size_t + SendPacket (const char *payload); + + size_t + SendPacket (const char *payload, + size_t payload_length); + + size_t + SendPacketAndWaitForResponse (const char *send_payload, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async); + + size_t + SendPacketAndWaitForResponse (const char *send_payload, + size_t send_length, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async); + + lldb::StateType + SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process, + const char *packet_payload, + size_t packet_length, + StringExtractorGDBRemote &response); + + // Wait for a packet within 'nsec' seconds + size_t + WaitForPacket (StringExtractorGDBRemote &response, + uint32_t nsec); + + // Wait for a packet with an absolute timeout time. If 'timeout' is NULL + // wait indefinitely. + size_t + WaitForPacket (StringExtractorGDBRemote &response, + lldb_private::TimeValue* timeout); + + char + GetAck (uint32_t timeout_seconds); + + size_t + SendAck (char ack_char); + + char + CalculcateChecksum (const char *payload, + size_t payload_length); + + void + SetAckMode (bool enabled) + { + m_send_acks = enabled; + } + + bool + SendAsyncSignal (int signo); + + bool + SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out = NULL); + + bool + GetSequenceMutex(lldb_private::Mutex::Locker& locker); + + //------------------------------------------------------------------ + // Communication overrides + //------------------------------------------------------------------ + virtual void + AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast); + + + lldb::pid_t + GetCurrentProcessID (uint32_t timeout_seconds); + + bool + GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str); + + //------------------------------------------------------------------ + /// Sends a GDB remote protocol 'A' packet that delivers program + /// arguments to the remote server. + /// + /// @param[in] argv + /// A NULL terminated array of const C strings to use as the + /// arguments. + /// + /// @param[in] timeout_seconds + /// The number of seconds to wait for a response from the remote + /// server. + /// + /// @return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + //------------------------------------------------------------------ + int + SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds); + + //------------------------------------------------------------------ + /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the + /// environment that will get used when launching an application + /// in conjunction with the 'A' packet. This function can be called + /// multiple times in a row in order to pass on the desired + /// environment that the inferior should be launched with. + /// + /// @param[in] name_equal_value + /// A NULL terminated C string that contains a single enironment + /// in the format "NAME=VALUE". + /// + /// @param[in] timeout_seconds + /// The number of seconds to wait for a response from the remote + /// server. + /// + /// @return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + //------------------------------------------------------------------ + int + SendEnvironmentPacket (char const *name_equal_value, + uint32_t timeout_seconds); + + //------------------------------------------------------------------ + /// Sends a "vAttach:PID" where PID is in hex. + /// + /// @param[in] pid + /// A process ID for the remote gdb server to attach to. + /// + /// @param[in] timeout_seconds + /// The number of seconds to wait for a response from the remote + /// server. + /// + /// @param[out] response + /// The response received from the gdb server. If the return + /// value is zero, \a response will contain a stop reply + /// packet. + /// + /// @return + /// Zero if the attach was successful, or an error indicating + /// an error code. + //------------------------------------------------------------------ + int + SendAttach (lldb::pid_t pid, + uint32_t timeout_seconds, + StringExtractorGDBRemote& response); + + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds); + + bool + DeallocateMemory (lldb::addr_t addr, uint32_t timeout_seconds); + + bool + IsRunning() const + { + return m_is_running.GetValue(); + } + + bool + GetHostInfo (uint32_t timeout_seconds); + + bool + HostInfoIsValid () const + { + return m_pointer_byte_size != 0; + } + + const lldb_private::ArchSpec & + GetHostArchitecture (); + + const lldb_private::ConstString & + GetOSString (); + + const lldb_private::ConstString & + GetVendorString(); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize (); + +protected: + typedef std::list packet_collection; + + size_t + SendPacketNoLock (const char *payload, + size_t payload_length); + + size_t + WaitForPacketNoLock (StringExtractorGDBRemote &response, + lldb_private::TimeValue* timeout_time_ptr); + + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteCommunication can see and modify these + //------------------------------------------------------------------ + bool m_send_acks; + lldb_private::Listener m_rx_packet_listener; + lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time + lldb_private::Predicate m_is_running; + + // If we need to send a packet while the target is running, the m_async_XXX + // member variables take care of making this happen. + lldb_private::Mutex m_async_mutex; + lldb_private::Predicate m_async_packet_predicate; + std::string m_async_packet; + StringExtractorGDBRemote m_async_response; + uint32_t m_async_timeout; + int m_async_signal; // We were asked to deliver a signal to the inferior process. + + lldb_private::ArchSpec m_arch; // Results from the qHostInfo call + uint32_t m_cpusubtype; // Results from the qHostInfo call + lldb_private::ConstString m_os; // Results from the qHostInfo call + lldb_private::ConstString m_vendor; // Results from the qHostInfo call + lldb::ByteOrder m_byte_order; // Results from the qHostInfo call + uint32_t m_pointer_byte_size; // Results from the qHostInfo call + + +private: + //------------------------------------------------------------------ + // For GDBRemoteCommunication only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunication); +}; + +#endif // liblldb_GDBRemoteCommunication_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp new file mode 100644 index 000000000000..a64e74dc9633 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -0,0 +1,508 @@ +//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteRegisterContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +// Project includes +#include "StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ThreadGDBRemote.h" +#include "ARM_GCC_Registers.h" +#include "ARM_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteRegisterContext constructor +//---------------------------------------------------------------------- +GDBRemoteRegisterContext::GDBRemoteRegisterContext +( + ThreadGDBRemote &thread, + StackFrame *frame, + GDBRemoteDynamicRegisterInfo ®_info, + bool read_all_at_once +) : + RegisterContext (thread, frame), + m_reg_info (reg_info), + m_reg_valid (), + m_reg_data (), + m_read_all_at_once (read_all_at_once) +{ + // Resize our vector of bools to contain one bool for every register. + // We will use these boolean values to know when a register value + // is valid in m_reg_data. + m_reg_valid.resize (reg_info.GetNumRegisters()); + + // Make a heap based buffer that is big enough to store all registers + DataBufferSP reg_data_sp(new DataBufferHeap (reg_info.GetRegisterDataByteSize(), 0)); + m_reg_data.SetData (reg_data_sp); + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteRegisterContext::~GDBRemoteRegisterContext() +{ +} + +ProcessGDBRemote & +GDBRemoteRegisterContext::GetGDBProcess() +{ + return static_cast(m_thread.GetProcess()); +} + +ThreadGDBRemote & +GDBRemoteRegisterContext::GetGDBThread() +{ + return static_cast(m_thread); +} + +void +GDBRemoteRegisterContext::Invalidate () +{ + SetAllRegisterValid (false); +} + +void +GDBRemoteRegisterContext::SetAllRegisterValid (bool b) +{ + std::vector::iterator pos, end = m_reg_valid.end(); + for (pos = m_reg_valid.begin(); pos != end; ++pos) + *pos = b; +} + +size_t +GDBRemoteRegisterContext::GetRegisterCount () +{ + return m_reg_info.GetNumRegisters (); +} + +const lldb::RegisterInfo * +GDBRemoteRegisterContext::GetRegisterInfoAtIndex (uint32_t reg) +{ + return m_reg_info.GetRegisterInfoAtIndex (reg); +} + +size_t +GDBRemoteRegisterContext::GetRegisterSetCount () +{ + return m_reg_info.GetNumRegisterSets (); +} + + + +const lldb::RegisterSet * +GDBRemoteRegisterContext::GetRegisterSet (uint32_t reg_set) +{ + return m_reg_info.GetRegisterSet (reg_set); +} + + + +bool +GDBRemoteRegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + // Read the register + if (ReadRegisterBytes (reg, m_reg_data)) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + uint32_t offset = reg_info->byte_offset; + switch (reg_info->encoding) + { + case eEncodingUint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = m_reg_data.GetMaxU32 (&offset, reg_info->byte_size); + return true; + + case 8: + value = m_reg_data.GetMaxU64 (&offset, reg_info->byte_size); + return true; + } + break; + + case eEncodingSint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (int32_t)m_reg_data.GetMaxU32 (&offset, reg_info->byte_size); + return true; + + case 8: + value = m_reg_data.GetMaxS64 (&offset, reg_info->byte_size); + return true; + } + break; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + value = m_reg_data.GetFloat (&offset); + return true; + + case sizeof (double): + value = m_reg_data.GetDouble (&offset); + return true; + + case sizeof (long double): + value = m_reg_data.GetLongDouble (&offset); + return true; + } + break; + } + } + return false; +} + + +bool +GDBRemoteRegisterContext::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (gdb_comm.IsRunning()) +// return false; + + if (m_reg_valid_stop_id != m_thread.GetProcess().GetStopID()) + { + Invalidate(); + m_reg_valid_stop_id = m_thread.GetProcess().GetStopID(); + } + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + assert (reg_info); + if (m_reg_valid[reg] == false) + { + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker)) + { + if (GetGDBProcess().SetCurrentGDBRemoteThread(m_thread.GetID())) + { + char packet[32]; + StringExtractorGDBRemote response; + int packet_len; + if (m_read_all_at_once) + { + // Get all registers in one packet + packet_len = ::snprintf (packet, sizeof(packet), "g"); + assert (packet_len < (sizeof(packet) - 1)); + if (gdb_comm.SendPacketAndWaitForResponse(packet, response, 1, false)) + { + if (response.IsNormalPacket()) + if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize()) + SetAllRegisterValid (true); + } + } + else + { + // Get each register individually + packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg, false); + assert (packet_len < (sizeof(packet) - 1)); + if (gdb_comm.SendPacketAndWaitForResponse(packet, response, 1, false)) + if (response.GetHexBytes ((uint8_t*)m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), reg_info->byte_size, '\xcc') == reg_info->byte_size) + m_reg_valid[reg] = true; + } + } + } + } + + bool reg_is_valid = m_reg_valid[reg]; + if (reg_is_valid) + { + if (&data != &m_reg_data) + { + // If we aren't extracting into our own buffer (which + // only happens when this function is called from + // ReadRegisterValue(uint32_t, Scalar&)) then + // we transfer bytes from our buffer into the data + // buffer that was passed in + data.SetByteOrder (m_reg_data.GetByteOrder()); + data.SetData (m_reg_data, reg_info->byte_offset, reg_info->byte_size); + } + } + return reg_is_valid; +} + + +bool +GDBRemoteRegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info) + { + DataExtractor data; + if (value.GetData (data, reg_info->byte_size)) + return WriteRegisterBytes (reg, data, 0); + } + return false; +} + + +bool +GDBRemoteRegisterContext::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (gdb_comm.IsRunning()) +// return false; + + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + + if (reg_info) + { + // Grab a pointer to where we are going to put this register + uint8_t *dst = (uint8_t *)m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size); + + if (dst == NULL) + return false; + + // Grab a pointer to where we are going to grab the new value from + const uint8_t *src = data.PeekData(0, reg_info->byte_size); + + if (src == NULL) + return false; + + if (data.GetByteOrder() == m_reg_data.GetByteOrder()) + { + // No swapping, just copy the bytes + ::memcpy (dst, src, reg_info->byte_size); + } + else + { + // Swap the bytes + for (uint32_t i=0; ibyte_size; ++i) + dst[i] = src[reg_info->byte_size - 1 - i]; + } + + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker)) + { + if (GetGDBProcess().SetCurrentGDBRemoteThread(m_thread.GetID())) + { + uint32_t offset, end_offset; + StreamString packet; + StringExtractorGDBRemote response; + if (m_read_all_at_once) + { + // Get all registers in one packet + packet.PutChar ('G'); + offset = 0; + end_offset = m_reg_data.GetByteSize(); + + packet.PutBytesAsRawHex8 (m_reg_data.GetDataStart(), + m_reg_data.GetByteSize(), + eByteOrderHost, + eByteOrderHost); + + // Invalidate all register values + Invalidate (); + + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + 1, + false)) + { + SetAllRegisterValid (false); + if (response.IsOKPacket()) + { + return true; + } + } + } + else + { + // Get each register individually + packet.Printf ("P%x=", reg); + packet.PutBytesAsRawHex8 (m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), + reg_info->byte_size, + eByteOrderHost, + eByteOrderHost); + + // Invalidate just this register + m_reg_valid[reg] = false; + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + 1, + false)) + { + if (response.IsOKPacket()) + { + return true; + } + } + } + } + } + } + return false; +} + + +bool +GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); + StringExtractorGDBRemote response; + if (gdb_comm.SendPacketAndWaitForResponse("g", response, 1, false)) + { + if (response.IsErrorPacket()) + return false; + + response.GetStringRef().insert(0, 1, 'G'); + data_sp.reset (new DataBufferHeap(response.GetStringRef().data(), + response.GetStringRef().size())); + return true; + } + return false; +} + +bool +GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); + StringExtractorGDBRemote response; + if (gdb_comm.SendPacketAndWaitForResponse((const char *)data_sp->GetBytes(), + data_sp->GetByteSize(), + response, + 1, + false)) + { + if (response.IsOKPacket()) + return true; + } + return false; +} + + +uint32_t +GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_reg_info.ConvertRegisterKindToRegisterNumber (kind, num); +} + +void +GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters() +{ + static lldb::RegisterInfo + g_register_infos[] = + { + // NAME ALT SZ OFF ENCODING FORMAT NUM COMPILER DWARF GENERIC + // ====== ======= == ==== ============= ============ === =============== =============== ========= + { "r0", NULL, 4, 0, eEncodingUint, eFormatHex, 0, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM }}, + { "r1", NULL, 4, 4, eEncodingUint, eFormatHex, 1, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM }}, + { "r2", NULL, 4, 8, eEncodingUint, eFormatHex, 2, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM }}, + { "r3", NULL, 4, 12, eEncodingUint, eFormatHex, 3, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM }}, + { "r4", NULL, 4, 16, eEncodingUint, eFormatHex, 4, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM }}, + { "r5", NULL, 4, 20, eEncodingUint, eFormatHex, 5, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM }}, + { "r6", NULL, 4, 24, eEncodingUint, eFormatHex, 6, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM }}, + { "r7", NULL, 4, 28, eEncodingUint, eFormatHex, 7, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP }}, + { "r8", NULL, 4, 32, eEncodingUint, eFormatHex, 8, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM }}, + { "r9", NULL, 4, 36, eEncodingUint, eFormatHex, 9, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM }}, + { "r10", NULL, 4, 40, eEncodingUint, eFormatHex, 10, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM }}, + { "r11", NULL, 4, 44, eEncodingUint, eFormatHex, 11, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM }}, + { "r12", NULL, 4, 48, eEncodingUint, eFormatHex, 12, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM }}, + { "sp", "r13", 4, 52, eEncodingUint, eFormatHex, 13, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP }}, + { "lr", "r14", 4, 56, eEncodingUint, eFormatHex, 14, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA }}, + { "pc", "r15", 4, 60, eEncodingUint, eFormatHex, 15, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC }}, + { NULL, NULL, 12, 64, eEncodingIEEE754, eFormatFloat, 16, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 76, eEncodingIEEE754, eFormatFloat, 17, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 88, eEncodingIEEE754, eFormatFloat, 18, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 100, eEncodingIEEE754, eFormatFloat, 19, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 112, eEncodingIEEE754, eFormatFloat, 20, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 124, eEncodingIEEE754, eFormatFloat, 21, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 136, eEncodingIEEE754, eFormatFloat, 22, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 148, eEncodingIEEE754, eFormatFloat, 23, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 160, eEncodingIEEE754, eFormatFloat, 24, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { "cpsr", "psr", 4, 172, eEncodingUint, eFormatHex, 25, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS }}, + { "s0", NULL, 4, 176, eEncodingIEEE754, eFormatFloat, 26, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM }}, + { "s1", NULL, 4, 180, eEncodingIEEE754, eFormatFloat, 27, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM }}, + { "s2", NULL, 4, 184, eEncodingIEEE754, eFormatFloat, 28, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM }}, + { "s3", NULL, 4, 188, eEncodingIEEE754, eFormatFloat, 29, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM }}, + { "s4", NULL, 4, 192, eEncodingIEEE754, eFormatFloat, 30, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM }}, + { "s5", NULL, 4, 196, eEncodingIEEE754, eFormatFloat, 31, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM }}, + { "s6", NULL, 4, 200, eEncodingIEEE754, eFormatFloat, 32, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM }}, + { "s7", NULL, 4, 204, eEncodingIEEE754, eFormatFloat, 33, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM }}, + { "s8", NULL, 4, 208, eEncodingIEEE754, eFormatFloat, 34, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM }}, + { "s9", NULL, 4, 212, eEncodingIEEE754, eFormatFloat, 35, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM }}, + { "s10", NULL, 4, 216, eEncodingIEEE754, eFormatFloat, 36, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM }}, + { "s11", NULL, 4, 220, eEncodingIEEE754, eFormatFloat, 37, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM }}, + { "s12", NULL, 4, 224, eEncodingIEEE754, eFormatFloat, 38, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM }}, + { "s13", NULL, 4, 228, eEncodingIEEE754, eFormatFloat, 39, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM }}, + { "s14", NULL, 4, 232, eEncodingIEEE754, eFormatFloat, 40, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM }}, + { "s15", NULL, 4, 236, eEncodingIEEE754, eFormatFloat, 41, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM }}, + { "s16", NULL, 4, 240, eEncodingIEEE754, eFormatFloat, 42, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM }}, + { "s17", NULL, 4, 244, eEncodingIEEE754, eFormatFloat, 43, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM }}, + { "s18", NULL, 4, 248, eEncodingIEEE754, eFormatFloat, 44, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM }}, + { "s19", NULL, 4, 252, eEncodingIEEE754, eFormatFloat, 45, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM }}, + { "s20", NULL, 4, 256, eEncodingIEEE754, eFormatFloat, 46, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM }}, + { "s21", NULL, 4, 260, eEncodingIEEE754, eFormatFloat, 47, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM }}, + { "s22", NULL, 4, 264, eEncodingIEEE754, eFormatFloat, 48, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM }}, + { "s23", NULL, 4, 268, eEncodingIEEE754, eFormatFloat, 49, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM }}, + { "s24", NULL, 4, 272, eEncodingIEEE754, eFormatFloat, 50, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM }}, + { "s25", NULL, 4, 276, eEncodingIEEE754, eFormatFloat, 51, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM }}, + { "s26", NULL, 4, 280, eEncodingIEEE754, eFormatFloat, 52, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM }}, + { "s27", NULL, 4, 284, eEncodingIEEE754, eFormatFloat, 53, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM }}, + { "s28", NULL, 4, 288, eEncodingIEEE754, eFormatFloat, 54, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM }}, + { "s29", NULL, 4, 292, eEncodingIEEE754, eFormatFloat, 55, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM }}, + { "s30", NULL, 4, 296, eEncodingIEEE754, eFormatFloat, 56, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM }}, + { "s31", NULL, 4, 300, eEncodingIEEE754, eFormatFloat, 57, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM }}, + { "fpscr", NULL, 4, 304, eEncodingUint, eFormatHex, 58, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM }}, + { "d16", NULL, 8, 308, eEncodingIEEE754, eFormatFloat, 59, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM }}, + { "d17", NULL, 8, 316, eEncodingIEEE754, eFormatFloat, 60, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM }}, + { "d18", NULL, 8, 324, eEncodingIEEE754, eFormatFloat, 61, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM }}, + { "d19", NULL, 8, 332, eEncodingIEEE754, eFormatFloat, 62, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM }}, + { "d20", NULL, 8, 340, eEncodingIEEE754, eFormatFloat, 63, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM }}, + { "d21", NULL, 8, 348, eEncodingIEEE754, eFormatFloat, 64, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM }}, + { "d22", NULL, 8, 356, eEncodingIEEE754, eFormatFloat, 65, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM }}, + { "d23", NULL, 8, 364, eEncodingIEEE754, eFormatFloat, 66, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM }}, + { "d24", NULL, 8, 372, eEncodingIEEE754, eFormatFloat, 67, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM }}, + { "d25", NULL, 8, 380, eEncodingIEEE754, eFormatFloat, 68, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM }}, + { "d26", NULL, 8, 388, eEncodingIEEE754, eFormatFloat, 69, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM }}, + { "d27", NULL, 8, 396, eEncodingIEEE754, eFormatFloat, 70, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM }}, + { "d28", NULL, 8, 404, eEncodingIEEE754, eFormatFloat, 71, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM }}, + { "d29", NULL, 8, 412, eEncodingIEEE754, eFormatFloat, 72, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM }}, + { "d30", NULL, 8, 420, eEncodingIEEE754, eFormatFloat, 73, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM }}, + { "d31", NULL, 8, 428, eEncodingIEEE754, eFormatFloat, 74, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM }}, + }; + static const uint32_t num_registers = sizeof (g_register_infos)/sizeof (lldb::RegisterInfo); + static ConstString gpr_reg_set ("General Purpose Registers"); + static ConstString vfp_reg_set ("Floating Point Registers"); + for (uint32_t i=0; i + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/RegisterContext.h" + + +class ThreadGDBRemote; +class ProcessGDBRemote; + +class GDBRemoteDynamicRegisterInfo +{ +public: + GDBRemoteDynamicRegisterInfo () : + m_regs (), + m_sets (), + m_set_reg_nums (), + m_reg_names (), + m_reg_alt_names (), + m_set_names (), + m_reg_data_byte_size (0) + { + } + + ~GDBRemoteDynamicRegisterInfo () + { + } + + void + AddRegister (lldb::RegisterInfo ®_info, lldb_private::ConstString ®_name, lldb_private::ConstString ®_alt_name, lldb_private::ConstString &set_name) + { + const uint32_t reg_num = m_regs.size(); + m_reg_names.push_back (reg_name); + m_reg_alt_names.push_back (reg_alt_name); + reg_info.name = reg_name.AsCString(); + assert (reg_info.name); + reg_info.alt_name = reg_alt_name.AsCString(NULL); + m_regs.push_back (reg_info); + uint32_t set = GetRegisterSetIndexByName (set_name, true); + assert (set < m_sets.size()); + assert (set < m_set_reg_nums.size()); + assert (set < m_set_names.size()); + m_set_reg_nums[set].push_back(reg_num); + size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; + } + + void + Finalize () + { + for (uint32_t set = 0; set < m_sets.size(); ++set) + { + assert (m_sets.size() == m_set_reg_nums.size()); + m_sets[set].num_registers = m_set_reg_nums[set].size(); + m_sets[set].registers = &m_set_reg_nums[set][0]; + } + } + + size_t + GetNumRegisters() const + { + return m_regs.size(); + } + + size_t + GetNumRegisterSets() const + { + return m_sets.size(); + } + + size_t + GetRegisterDataByteSize() const + { + return m_reg_data_byte_size; + } + + const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t i) const + { + if (i < m_regs.size()) + return &m_regs[i]; + return NULL; + } + + const lldb::RegisterSet * + GetRegisterSet (uint32_t i) const + { + if (i < m_sets.size()) + return &m_sets[i]; + return NULL; + } + + uint32_t + GetRegisterSetIndexByName (lldb_private::ConstString &set_name, bool can_create) + { + name_collection::iterator pos, end = m_set_names.end(); + for (pos = m_set_names.begin(); pos != end; ++pos) + { + if (*pos == set_name) + return std::distance (m_set_names.begin(), pos); + } + + m_set_names.push_back(set_name); + m_set_reg_nums.resize(m_set_reg_nums.size()+1); + lldb::RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; + m_sets.push_back (new_set); + return m_sets.size() - 1; + } + + uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const + { + reg_collection::const_iterator pos, end = m_regs.end(); + for (pos = m_regs.begin(); pos != end; ++pos) + { + if (pos->kinds[kind] == num) + return std::distance (m_regs.begin(), pos); + } + + return LLDB_INVALID_REGNUM; + } + void + Clear() + { + m_regs.clear(); + m_sets.clear(); + m_set_reg_nums.clear(); + m_reg_names.clear(); + m_reg_alt_names.clear(); + m_set_names.clear(); + } + + void + HardcodeARMRegisters(); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteRegisterContext can see and modify these + //------------------------------------------------------------------ + typedef std::vector reg_collection; + typedef std::vector set_collection; + typedef std::vector reg_num_collection; + typedef std::vector set_reg_num_collection; + typedef std::vector name_collection; + + reg_collection m_regs; + set_collection m_sets; + set_reg_num_collection m_set_reg_nums; + name_collection m_reg_names; + name_collection m_reg_alt_names; + name_collection m_set_names; + size_t m_reg_data_byte_size; // The number of bytes required to store all registers +}; + +class GDBRemoteRegisterContext : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteRegisterContext (ThreadGDBRemote &thread, + lldb_private::StackFrame *frame, + GDBRemoteDynamicRegisterInfo ®_info, + bool read_all_at_once); + + virtual + ~GDBRemoteRegisterContext (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t reg_set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +protected: + + void + SetAllRegisterValid (bool b); + + ProcessGDBRemote & + GetGDBProcess(); + + ThreadGDBRemote & + GetGDBThread(); + + GDBRemoteDynamicRegisterInfo &m_reg_info; + std::vector m_reg_valid; + uint32_t m_reg_valid_stop_id; + lldb_private::DataExtractor m_reg_data; + bool m_read_all_at_once; + +private: + //------------------------------------------------------------------ + // For GDBRemoteRegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteRegisterContext); +}; + +#endif // lldb_GDBRemoteRegisterContext_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp new file mode 100644 index 000000000000..a88ec7b09d4c --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp @@ -0,0 +1,1148 @@ +//===-- GDBServer.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 +#include +#include + +#include "GDBServerLog.h" +#include "GDBRemoteSession.h" + +using namespace lldb; + +//---------------------------------------------------------------------- +// Run loop modes which determine which run loop function will be called +//---------------------------------------------------------------------- +typedef enum +{ + eDCGSRunLoopModeInvalid = 0, + eDCGSRunLoopModeGetStartModeFromRemoteProtocol, + eDCGSRunLoopModeInferiorAttaching, + eDCGSRunLoopModeInferiorLaunching, + eDCGSRunLoopModeInferiorExecuting, + eDCGSRunLoopModeInferiorKillOrDetach, + eDCGSRunLoopModeExit +} GSRunLoopMode; + +typedef enum +{ + eLaunchFlavorDefault = 0, + eLaunchFlavorPosixSpawn, +#if defined (__arm__) + eLaunchFlavorSpringBoard, +#endif + eLaunchFlavorForkExec, +} GSLaunchFlavor; + +typedef lldb::shared_ptr GDBRemoteSP; + +typedef struct HandleBroadcastEventInfo +{ + TargetSP target_sp; + GDBRemoteSP remote_sp; + GSRunLoopMode mode; + + Target * + GetTarget () + { + return target_sp.get(); + } + + Process * + GetProcess() + { + if (target_sp.get()) + return target_sp->GetProcess().get(); + return NULL; + } + + GDBRemoteSession * + GetRemote () + { + return remote_sp.get(); + } + +}; + + +//---------------------------------------------------------------------- +// Global Variables +//---------------------------------------------------------------------- +static int g_lockdown_opt = 0; +static int g_applist_opt = 0; +static GSLaunchFlavor g_launch_flavor = eLaunchFlavorDefault; +int g_isatty = 0; + +//---------------------------------------------------------------------- +// Run Loop function prototypes +//---------------------------------------------------------------------- +void GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info); +void GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info); + + +//---------------------------------------------------------------------- +// Get our program path and arguments from the remote connection. +// We will need to start up the remote connection without a PID, get the +// arguments, wait for the new process to finish launching and hit its +// entry point, and then return the run loop mode that should come next. +//---------------------------------------------------------------------- +void +GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info) +{ + std::string packet; + + Target *target = info->GetTarget(); + GDBRemoteSession *remote = info->GetRemote(); + if (target != NULL && remote != NULL) + { + // Spin waiting to get the A packet. + while (1) + { + gdb_err_t err = gdb_err; + GDBRemoteSession::PacketEnum type; + + err = remote->HandleReceivedPacket (&type); + + // check if we tried to attach to a process + if (type == GDBRemoteSession::vattach || type == GDBRemoteSession::vattachwait) + { + if (err == gdb_success) + { + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + } + else + { + Log::STDERR ("error: attach failed."); + info->mode = eDCGSRunLoopModeExit; + return; + } + } + + if (err == gdb_success) + { + // If we got our arguments we are ready to launch using the arguments + // and any environment variables we received. + if (type == GDBRemoteSession::set_argv) + { + info->mode = eDCGSRunLoopModeInferiorLaunching; + return; + } + } + else if (err == gdb_not_connected) + { + Log::STDERR ("error: connection lost."); + info->mode = eDCGSRunLoopModeExit; + return; + } + else + { + // a catch all for any other gdb remote packets that failed + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.",__FUNCTION__); + continue; + } + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__); + } + } + info->mode = eDCGSRunLoopModeExit; +} + + +//---------------------------------------------------------------------- +// This run loop mode will wait for the process to launch and hit its +// entry point. It will currently ignore all events except for the +// process state changed event, where it watches for the process stopped +// or crash process state. +//---------------------------------------------------------------------- +GSRunLoopMode +GSRunLoopLaunchInferior (HandleBroadcastEventInfo *info) +{ + // The Process stuff takes a c array, the GSContext has a vector... + // So make up a c array. + Target *target = info->GetTarget(); + GDBRemoteSession *remote = info->GetRemote(); + Process* process = info->GetProcess(); + + if (process == NULL) + return eDCGSRunLoopModeExit; + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Launching '%s'...", __FUNCTION__, target->GetExecutableModule()->GetFileSpec().GetFilename().AsCString()); + + // Our launch type hasn't been set to anything concrete, so we need to + // figure our how we are going to launch automatically. + + GSLaunchFlavor launch_flavor = g_launch_flavor; + if (launch_flavor == eLaunchFlavorDefault) + { + // Our default launch method is posix spawn + launch_flavor = eLaunchFlavorPosixSpawn; + +#if defined (__arm__) + // Check if we have an app bundle, if so launch using SpringBoard. + if (strstr(inferior_argv[0], ".app")) + { + launch_flavor = eLaunchFlavorSpringBoard; + } +#endif + } + + //ctx.SetLaunchFlavor(launch_flavor); + + const char *stdio_file = NULL; + lldb::pid_t pid = process->Launch (remote->GetARGV(), remote->GetENVP(), stdio_file, stdio_file, stdio_file); + + if (pid == LLDB_INVALID_PROCESS_ID) + { + Log::STDERR ("error: process launch failed: %s", process->GetError().AsCString()); + } + else + { + if (remote->IsConnected()) + { + // It we are connected already, the next thing gdb will do is ask + // whether the launch succeeded, and if not, whether there is an + // error code. So we need to fetch one packet from gdb before we wait + // on the stop from the target. + gdb_err_t err = gdb_err; + GDBRemoteSession::PacketEnum type; + + err = remote->HandleReceivedPacket (&type); + + if (err != gdb_success) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.", __FUNCTION__); + return eDCGSRunLoopModeExit; + } + if (type != GDBRemoteSession::query_launch_success) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__); + } + } + } + + Listener listener("GSRunLoopLaunchInferior"); + listener.StartListeningForEvents (process, Process::eBroadcastBitStateChanged); + while (process->GetID() != LLDB_INVALID_PROCESS_ID) + { + uint32_t event_mask = 0; + while (listener.WaitForEvent(NULL, &event_mask)) + { + if (event_mask & Process::eBroadcastBitStateChanged) + { + Event event; + StateType event_state; + while ((event_state = process->GetNextEvent (&event))) + if (StateIsStoppedState(event_state)) + { + GDBServerLog::LogIf (GS_LOG_EVENTS, "%s process %4.4x stopped with state %s", __FUNCTION__, pid, StateAsCString(event_state)); + + switch (event_state) + { + default: + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateSuspended: + break; // Ignore + + case eStateRunning: + case eStateStepping: + // Still waiting to stop at entry point... + break; + + case eStateStopped: + case eStateCrashed: + return eDCGSRunLoopModeInferiorExecuting; + + case eStateDetached: + case eStateExited: + pid = LLDB_INVALID_PROCESS_ID; + return eDCGSRunLoopModeExit; + } + } + + if (event_state = eStateInvalid) + break; + } + } + } + + return eDCGSRunLoopModeExit; +} + + +//---------------------------------------------------------------------- +// This run loop mode will wait for the process to launch and hit its +// entry point. It will currently ignore all events except for the +// process state changed event, where it watches for the process stopped +// or crash process state. +//---------------------------------------------------------------------- +GSRunLoopMode +GSRunLoopLaunchAttaching (HandleBroadcastEventInfo *info, lldb::pid_t& pid) +{ + Process* process = info->GetProcess(); + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, pid); + pid = process->Attach(pid); + + if (pid == LLDB_INVALID_PROCESS_ID) + return eDCGSRunLoopModeExit; + return eDCGSRunLoopModeInferiorExecuting; +} + +//---------------------------------------------------------------------- +// Watch for signals: +// SIGINT: so we can halt our inferior. (disabled for now) +// SIGPIPE: in case our child process dies +//---------------------------------------------------------------------- +lldb::pid_t g_pid; +int g_sigpipe_received = 0; +void +signal_handler(int signo) +{ + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (%s)", __FUNCTION__, Host::GetSignalAsCString(signo)); + + switch (signo) + { +// case SIGINT: +// DNBProcessKill (g_pid, signo); +// break; + + case SIGPIPE: + g_sigpipe_received = 1; + break; + } +} + +// Return the new run loop mode based off of the current process state +void +HandleProcessStateChange (HandleBroadcastEventInfo *info, bool initialize) +{ + Process *process = info->GetProcess(); + if (process == NULL) + { + info->mode = eDCGSRunLoopModeExit; + return; + } + + if (process->GetID() == LLDB_INVALID_PROCESS_ID) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__); + info->mode = eDCGSRunLoopModeExit; + return; + } + StateType pid_state = process->GetState (); + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (info, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, StateAsCString(pid_state)); + + switch (pid_state) + { + case eStateInvalid: + case eStateUnloaded: + // Something bad happened + info->mode = eDCGSRunLoopModeExit; + return; + + case eStateAttaching: + case eStateLaunching: + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + if (initialize == false) + { + // Compare the last stop count to our current notion of a stop count + // to make sure we don't notify more than once for a given stop. + static uint32_t g_prev_stop_id = 0; + uint32_t stop_id = process->GetStopID(); + bool pid_stop_count_changed = g_prev_stop_id != stop_id; + if (pid_stop_count_changed) + { + info->GetRemote()->FlushSTDIO(); + + if (stop_id == 1) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); + } + else + { + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); + info->GetRemote()->NotifyThatProcessStopped (); + } + } + else + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); + } + } + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + + case eStateStepping: + case eStateRunning: + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + + case eStateExited: + info->GetRemote()->HandlePacket_last_signal (NULL); + info->mode = eDCGSRunLoopModeExit; + return; + + } + + // Catch all... + info->mode = eDCGSRunLoopModeExit; +} + +bool +CommunicationHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton) +{ + HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton; + Process *process = info->GetProcess(); + + if (process == NULL) + { + info->mode = eDCGSRunLoopModeExit; + return true; + } + + if (event_mask & Communication::eBroadcastBitPacketAvailable) + { + if (process->IsRunning()) + { + if (info->GetRemote()->HandleAsyncPacket() == gdb_not_connected) + info->mode = eDCGSRunLoopModeExit; + } + else + { + if (info->GetRemote()->HandleReceivedPacket() == gdb_not_connected) + info->mode = eDCGSRunLoopModeExit; + } + } + if (event_mask & Communication::eBroadcastBitReadThreadDidExit) + { + info->mode = eDCGSRunLoopModeExit; + } + if (event_mask & Communication::eBroadcastBitDisconnected) + { + info->mode = eDCGSRunLoopModeExit; + } + + return true; + +} + +bool +ProcessHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton) +{ + HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton; + Process *process = info->GetProcess(); + if (process == NULL) + { + info->mode = eDCGSRunLoopModeExit; + return true; + } + + if (event_mask & Process::eBroadcastBitStateChanged) + { + // Consume all available process events with no timeout + Event event; + StateType process_state; + while ((process_state = process->GetNextEvent (&event)) != eStateInvalid) + { + if (StateIsStoppedState(process_state)) + info->GetRemote()->FlushSTDIO(); + HandleProcessStateChange (info, false); + + if (info->mode != eDCGSRunLoopModeInferiorExecuting) + break; + } + } + else + if (event_mask & (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitSTDERR)) + { + info->GetRemote()->FlushSTDIO(); + } + return true; +} + +// This function handles the case where our inferior program is stopped and +// we are waiting for gdb remote protocol packets. When a packet occurs that +// makes the inferior run, we need to leave this function with a new state +// as the return code. +void +GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info) +{ + GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__); + + // Init our mode and set 'is_running' based on the current process state + HandleProcessStateChange (info, true); + + uint32_t desired_mask, acquired_mask; + Listener listener("GSRunLoopInferiorExecuting"); + + desired_mask = Communication::eBroadcastBitPacketAvailable | + Communication::eBroadcastBitReadThreadDidExit | + Communication::eBroadcastBitDisconnected; + + acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()), + desired_mask, + CommunicationHandleBroadcastEvent, + info); + + assert (acquired_mask == desired_mask); + desired_mask = GDBRemotePacket::eBroadcastBitPacketAvailable; + + acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()), + desired_mask, + CommunicationHandleBroadcastEvent, + info); + + assert (acquired_mask == desired_mask); + + desired_mask = Process::eBroadcastBitStateChanged | + Process::eBroadcastBitSTDOUT | + Process::eBroadcastBitSTDERR ; + acquired_mask = listener.StartListeningForEvents (info->GetProcess (), + desired_mask, + ProcessHandleBroadcastEvent, + info); + + assert (acquired_mask == desired_mask); + + Process *process = info->GetProcess(); + + while (process->IsAlive()) + { + if (!info->GetRemote()->IsConnected()) + { + info->mode = eDCGSRunLoopModeInferiorKillOrDetach; + break; + } + + // We want to make sure we consume all process state changes and have + // whomever is notifying us to wait for us to reset the event bit before + // continuing. + //ctx.Events().SetResetAckMask (GSContext::event_proc_state_changed); + uint32_t event_mask = 0; + Broadcaster *broadcaster = listener.WaitForEvent(NULL, &event_mask); + if (broadcaster) + { + listener.HandleBroadcastEvent(broadcaster, event_mask); + } + } +} + + +//---------------------------------------------------------------------- +// Convenience function to set up the remote listening port +// Returns 1 for success 0 for failure. +//---------------------------------------------------------------------- + +static bool +StartListening (HandleBroadcastEventInfo *info, int listen_port) +{ + if (!info->GetRemote()->IsConnected()) + { + Log::STDOUT ("Listening to port %i...\n", listen_port); + char connect_url[256]; + snprintf(connect_url, sizeof(connect_url), "listen://%i", listen_port); + + Communication &comm = info->remote_sp->GetPacketComm(); + comm.SetConnection (new ConnectionFileDescriptor); + + if (comm.Connect (connect_url)) + { + if (comm.StartReadThread()) + return true; + + Log::STDERR ("Failed to start the communication read thread.\n", connect_url); + comm.Disconnect(); + } + else + { + Log::STDERR ("Failed to connection to %s.\n", connect_url); + } + return false; + } + return true; +} + +//---------------------------------------------------------------------- +// ASL Logging callback that can be registered with DNBLogSetLogDCScriptInterpreter::Type +//---------------------------------------------------------------------- +//void +//ASLLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args) +//{ +// if (format == NULL) +// return; +// static aslmsg g_aslmsg = NULL; +// if (g_aslmsg == NULL) +// { +// g_aslmsg = ::asl_new (ASL_TYPE_MSG); +// char asl_key_sender[PATH_MAX]; +// snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.dc-gdbserver-%g", dc_gdbserverVersionNumber); +// ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); +// } +// +// int asl_level; +// if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT; +// else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR; +// else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING; +// else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO; +// else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG; +// +// ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); +//} + +//---------------------------------------------------------------------- +// FILE based Logging callback that can be registered with +// DNBLogSetLogDCScriptInterpreter::Type +//---------------------------------------------------------------------- +void +FileLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args) +{ + if (baton == NULL || format == NULL) + return; + + ::vfprintf ((FILE *)baton, format, args); + ::fprintf ((FILE *)baton, "\n"); +} + +//---------------------------------------------------------------------- +// option descriptors for getopt_long() +//---------------------------------------------------------------------- +static struct option g_long_options[] = +{ + { "arch", required_argument, NULL, 'c' }, + { "attach", required_argument, NULL, 'a' }, + { "debug", no_argument, NULL, 'g' }, + { "verbose", no_argument, NULL, 'v' }, + { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k" + { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t" + { "log-file", required_argument, NULL, 'l' }, + { "log-flags", required_argument, NULL, 'f' }, + { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only) + { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose namet starts with ARG + { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name + { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name + { NULL, 0, NULL, 0 } +}; + +extern const double dc_gdbserverVersionNumber; +int +main (int argc, char *argv[]) +{ + Initialize(); + Host::ThreadCreated ("[main]"); + + g_isatty = ::isatty (STDIN_FILENO); + +// signal (SIGINT, signal_handler); + signal (SIGPIPE, signal_handler); + + Log *log = GDBServerLog::GetLogIfAllCategoriesSet(GS_LOG_ALL); + const char *this_exe_name = argv[0]; + int i; + int attach_pid = LLDB_INVALID_PROCESS_ID; + for (i=0; iCreateProcess ()); + info.remote_sp.reset (new GDBRemoteSession (process_sp)); + + info.remote_sp->SetLog (log); + StreamString sstr; + sstr.Printf("ConnectionFileDescriptor(%s)", argv[0]); + + if (info.remote_sp.get() == NULL) + { + Log::STDERR ("error: failed to create a GDBRemoteSession class\n"); + return -1; + } + + + + // If we know we're waiting to attach, we don't need any of this other info. + if (start_mode != eDCGSRunLoopModeInferiorAttaching) + { + if (argc == 0 || g_lockdown_opt) + { + if (g_lockdown_opt != 0) + { + // Work around for SIGPIPE crashes due to posix_spawn issue. We have to close + // STDOUT and STDERR, else the first time we try and do any, we get SIGPIPE and + // die as posix_spawn is doing bad things with our file descriptors at the moment. + int null = open("/dev/null", O_RDWR); + dup2(null, STDOUT_FILENO); + dup2(null, STDERR_FILENO); + } + else if (g_applist_opt != 0) + { +// // List all applications we are able to see +// std::string applist_plist; +// int err = ListApplications(applist_plist, false, false); +// if (err == 0) +// { +// fputs (applist_plist.c_str(), stdout); +// } +// else +// { +// Log::STDERR ("error: ListApplications returned error %i\n", err); +// } +// // Exit with appropriate error if we were asked to list the applications +// // with no other args were given (and we weren't trying to do this over +// // lockdown) +// return err; + return 0; + } + + //DNBLogDebug("Get args from remote protocol..."); + start_mode = eDCGSRunLoopModeGetStartModeFromRemoteProtocol; + } + else + { + start_mode = eDCGSRunLoopModeInferiorLaunching; + // Fill in the argv array in the context from the rest of our args. + // Skip the name of this executable and the port number + info.remote_sp->SetArguments (argc, argv); + } + } + + if (start_mode == eDCGSRunLoopModeExit) + return -1; + + info.mode = start_mode; + + while (info.mode != eDCGSRunLoopModeExit) + { + switch (info.mode) + { + case eDCGSRunLoopModeGetStartModeFromRemoteProtocol: + #if defined (__arm__) + if (g_lockdown_opt) + { + if (!info.remote_sp->GetCommunication()->IsConnected()) + { + if (info.remote_sp->GetCommunication()->ConnectToService () != gdb_success) + { + Log::STDERR ("Failed to get connection from a remote gdb process.\n"); + info.mode = eDCGSRunLoopModeExit; + } + else if (g_applist_opt != 0) + { + // List all applications we are able to see + std::string applist_plist; + if (ListApplications(applist_plist, false, false) == 0) + { + //DNBLogDebug("Task list: %s", applist_plist.c_str()); + + info.remote_sp->GetCommunication()->Write(applist_plist.c_str(), applist_plist.size()); + // Issue a read that will never yield any data until the other side + // closes the socket so this process doesn't just exit and cause the + // socket to close prematurely on the other end and cause data loss. + std::string buf; + info.remote_sp->GetCommunication()->Read(buf); + } + info.remote_sp->GetCommunication()->Disconnect(false); + info.mode = eDCGSRunLoopModeExit; + break; + } + else + { + // Start watching for remote packets + info.remote_sp->StartReadRemoteDataThread(); + } + } + } + else +#endif + { + if (StartListening (&info, listen_port)) + Log::STDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); + else + info.mode = eDCGSRunLoopModeExit; + } + + if (info.mode != eDCGSRunLoopModeExit) + GSRunLoopGetStartModeFromRemote (&info); + break; + + case eDCGSRunLoopModeInferiorAttaching: + if (!waitfor_pid_name.empty()) + { + // Set our end wait time if we are using a waitfor-duration + // option that may have been specified + + TimeValue attach_timeout_abstime; + if (waitfor_duration != 0) + { + attach_timeout_abstime = TimeValue::Now(); + attach_timeout_abstime.OffsetWithSeconds (waitfor_duration); + } + GSLaunchFlavor launch_flavor = g_launch_flavor; + if (launch_flavor == eLaunchFlavorDefault) + { + // Our default launch method is posix spawn + launch_flavor = eLaunchFlavorPosixSpawn; + +#if defined (__arm__) + // Check if we have an app bundle, if so launch using SpringBoard. + if (waitfor_pid_name.find (".app") != std::string::npos) + { + launch_flavor = eLaunchFlavorSpringBoard; + } +#endif + } + + //ctx.SetLaunchFlavor(launch_flavor); + + + lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str()); + if (pid == LLDB_INVALID_PROCESS_ID) + { + info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError(); + Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString()); + info.mode = eDCGSRunLoopModeExit; + } + else + { + info.mode = eDCGSRunLoopModeInferiorExecuting; + } + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + Log::STDOUT ("Attaching to process %i...\n", attach_pid); + info.mode = GSRunLoopLaunchAttaching (&info, attach_pid); + if (info.mode != eDCGSRunLoopModeInferiorExecuting) + { + const char *error_str = info.GetRemote()->GetLaunchError().AsCString(); + Log::STDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error."); + info.mode = eDCGSRunLoopModeExit; + } + } + else if (!attach_pid_name.empty ()) + { + lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str()); + if (pid == LLDB_INVALID_PROCESS_ID) + { + info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError(); + Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString()); + info.mode = eDCGSRunLoopModeExit; + } + else + { + info.mode = eDCGSRunLoopModeInferiorExecuting; + } + } + else + { + Log::STDERR ("error: asked to attach with empty name and invalid PID."); + info.mode = eDCGSRunLoopModeExit; + } + + if (info.mode != eDCGSRunLoopModeExit) + { + if (StartListening (&info, listen_port)) + Log::STDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid); + else + info.mode = eDCGSRunLoopModeExit; + } + break; + + case eDCGSRunLoopModeInferiorLaunching: + info.mode = GSRunLoopLaunchInferior (&info); + + if (info.mode == eDCGSRunLoopModeInferiorExecuting) + { + if (StartListening (&info, listen_port)) + Log::STDOUT ("Got a connection, waiting for debugger instructions for task \"%s\".\n", argv[0]); + else + info.mode = eDCGSRunLoopModeExit; + } + else + { + Log::STDERR ("error: failed to launch process %s: %s\n", argv[0], info.GetRemote()->GetLaunchError().AsCString()); + } + break; + + case eDCGSRunLoopModeInferiorExecuting: + GSRunLoopInferiorExecuting (&info); + break; + + case eDCGSRunLoopModeInferiorKillOrDetach: + { + Process *process = info.GetProcess(); + if (process && process->IsAlive()) + { + process->Kill(SIGCONT); + process->Kill(SIGKILL); + } + } + info.mode = eDCGSRunLoopModeExit; + break; + + default: + info.mode = eDCGSRunLoopModeExit; + case eDCGSRunLoopModeExit: + break; + } + } + + return 0; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp new file mode 100644 index 000000000000..2d4116ebe7bf --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp @@ -0,0 +1,80 @@ +//===-- GDBServerLog.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// GDBServerLog.cpp +// liblldb +// +// Created by Greg Clayton on 6/19/09. +// +// +//---------------------------------------------------------------------- + +#include "GDBServerLog.h" + +using namespace lldb; + +static Log * +LogAccessor (bool get, Log *log) +{ + static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. + if (get) + { +// // Debug code below for enabling logging by default +// if (g_log == NULL) +// { +// g_log = new Log("/dev/stdout", false); +// g_log->GetMask().SetAllFlagBits(GS_LOG_ALL); +// g_log->GetOptions().Set(LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_THREAD_NAME); +// } + } + else + { + if (g_log) + delete g_log; + g_log = log; + } + + return g_log; +} + +Log * +GDBServerLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +GDBServerLog::SetLog (Log *log) +{ + LogAccessor (false, log); +} + + +void +GDBServerLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = GDBServerLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h new file mode 100644 index 000000000000..3dec8088cef3 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h @@ -0,0 +1,55 @@ +//===-- GDBServerLog.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// GDBServerLog.h +// liblldb +// +// Created by Greg Clayton on 6/19/09. +// +// +//---------------------------------------------------------------------- + +#ifndef liblldb_GDBServerLog_h_ +#define liblldb_GDBServerLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/Core/Log.h" + +// Project includes +#define GS_LOG_VERBOSE (1u << 0) +#define GS_LOG_DEBUG (1u << 1) +#define GS_LOG_PACKETS (1u << 2) +#define GS_LOG_EVENTS (1u << 3) +#define GS_LOG_MINIMAL (1u << 4) +#define GS_LOG_ALL (UINT32_MAX) +#define GS_LOG_DEFAULT (GS_LOG_VERBOSE |\ + GS_LOG_PACKETS) + +namespace lldb { + +class GDBServerLog +{ +public: + static Log * + GetLog (uint32_t mask = 0); + + static void + SetLog (Log *log); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +} // namespace lldb + +#endif // liblldb_GDBServerLog_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp new file mode 100644 index 000000000000..e08679c1e1c3 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -0,0 +1,2272 @@ +//===-- ProcessGDBRemote.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 +#include +#include + +// Other libraries and framework includes + +#include "lldb/Breakpoint/WatchpointLocation.h" +#include "lldb/Core/Args.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "PseudoTerminal.h" + +// Project includes +#include "lldb/Host/Host.h" +#include "StringExtractorGDBRemote.h" +#include "GDBRemoteRegisterContext.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "libunwind.h" +#include "MacOSXLibunwindCallbacks.h" + +#if defined (__i386__) || defined (__x86_64__) +#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_I386_BPT +#define MACH_EXC_DATA0_TRACE EXC_I386_SGL +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) +#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_PPC_BREAKPOINT +#elif defined (__arm__) +#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_ARM_BREAKPOINT +#endif + + +#define DEBUGSERVER_BASENAME "debugserver" +using namespace lldb; +using namespace lldb_private; + +static inline uint16_t +get_random_port () +{ + return (arc4random() % (UINT16_MAX - 1000u)) + 1000u; +} + + +const char * +ProcessGDBRemote::GetPluginNameStatic() +{ + return "process.gdb-remote"; +} + +const char * +ProcessGDBRemote::GetPluginDescriptionStatic() +{ + return "GDB Remote protocol based debugging plug-in."; +} + +void +ProcessGDBRemote::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance); +} + + +Process* +ProcessGDBRemote::CreateInstance (Target &target, Listener &listener) +{ + return new ProcessGDBRemote (target, listener); +} + +bool +ProcessGDBRemote::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +//---------------------------------------------------------------------- +// ProcessGDBRemote constructor +//---------------------------------------------------------------------- +ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : + Process (target, listener), + m_dynamic_loader_ap (), + m_byte_order (eByteOrderHost), + m_flags (0), + m_stdio_communication ("gdb-remote.stdio"), + m_stdio_mutex (Mutex::eMutexTypeRecursive), + m_stdout_data (), + m_arch_spec (), + m_gdb_comm(), + m_debugserver_pid (LLDB_INVALID_PROCESS_ID), + m_debugserver_monitor (0), + m_register_info (), + m_curr_tid (LLDB_INVALID_THREAD_ID), + m_curr_tid_run (LLDB_INVALID_THREAD_ID), + m_async_broadcaster ("lldb.process.gdb-remote.async-broadcaster"), + m_async_thread (LLDB_INVALID_HOST_THREAD), + m_z0_supported (1), + m_continue_packet(), + m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), + m_libunwind_target_type (UNW_TARGET_UNSPECIFIED), + m_libunwind_addr_space (NULL), + m_waiting_for_attach (false), + m_packet_timeout (1), + m_max_memory_size (512) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessGDBRemote::~ProcessGDBRemote() +{ + // m_mach_process.UnregisterNotificationCallbacks (this); + Clear(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +const char * +ProcessGDBRemote::GetPluginName() +{ + return "Process debugging plug-in that uses the GDB remote protocol"; +} + +const char * +ProcessGDBRemote::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessGDBRemote::GetPluginVersion() +{ + return 1; +} + +void +ProcessGDBRemote::GetPluginCommandHelp (const char *command, Stream *strm) +{ + strm->Printf("TODO: fill this in\n"); +} + +Error +ProcessGDBRemote::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in commands are currently supported."); + return error; +} + +Log * +ProcessGDBRemote::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + +void +ProcessGDBRemote::BuildDynamicRegisterInfo () +{ + char register_info_command[64]; + m_register_info.Clear(); + StringExtractorGDBRemote::Type packet_type = StringExtractorGDBRemote::eResponse; + uint32_t reg_offset = 0; + uint32_t reg_num = 0; + for (; packet_type == StringExtractorGDBRemote::eResponse; ++reg_num) + { + ::snprintf (register_info_command, sizeof(register_info_command), "qRegisterInfo%x", reg_num); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(register_info_command, response, 2, false)) + { + packet_type = response.GetType(); + if (packet_type == StringExtractorGDBRemote::eResponse) + { + std::string name; + std::string value; + ConstString reg_name; + ConstString alt_name; + ConstString set_name; + RegisterInfo reg_info = { NULL, // Name + NULL, // Alt name + 0, // byte size + reg_offset, // offset + eEncodingUint, // encoding + eFormatHex, // formate + reg_num, // native register number + { + LLDB_INVALID_REGNUM, // GCC reg num + LLDB_INVALID_REGNUM, // DWARF reg num + LLDB_INVALID_REGNUM, // generic reg num + reg_num // GDB reg num + } + }; + + while (response.GetNameColonValue(name, value)) + { + if (name.compare("name") == 0) + { + reg_name.SetCString(value.c_str()); + } + else if (name.compare("alt-name") == 0) + { + alt_name.SetCString(value.c_str()); + } + else if (name.compare("bitsize") == 0) + { + reg_info.byte_size = Args::StringToUInt32(value.c_str(), 0, 0) / CHAR_BIT; + } + else if (name.compare("offset") == 0) + { + uint32_t offset = Args::StringToUInt32(value.c_str(), UINT32_MAX, 0); + if (offset != offset) + { + reg_offset = offset; + reg_info.byte_offset = offset; + } + } + else if (name.compare("encoding") == 0) + { + if (value.compare("uint") == 0) + reg_info.encoding = eEncodingUint; + else if (value.compare("sint") == 0) + reg_info.encoding = eEncodingSint; + else if (value.compare("ieee754") == 0) + reg_info.encoding = eEncodingIEEE754; + else if (value.compare("vector") == 0) + reg_info.encoding = eEncodingVector; + } + else if (name.compare("format") == 0) + { + if (value.compare("binary") == 0) + reg_info.format = eFormatBinary; + else if (value.compare("decimal") == 0) + reg_info.format = eFormatDecimal; + else if (value.compare("hex") == 0) + reg_info.format = eFormatHex; + else if (value.compare("float") == 0) + reg_info.format = eFormatFloat; + else if (value.compare("vector-sint8") == 0) + reg_info.format = eFormatVectorOfSInt8; + else if (value.compare("vector-uint8") == 0) + reg_info.format = eFormatVectorOfUInt8; + else if (value.compare("vector-sint16") == 0) + reg_info.format = eFormatVectorOfSInt16; + else if (value.compare("vector-uint16") == 0) + reg_info.format = eFormatVectorOfUInt16; + else if (value.compare("vector-sint32") == 0) + reg_info.format = eFormatVectorOfSInt32; + else if (value.compare("vector-uint32") == 0) + reg_info.format = eFormatVectorOfUInt32; + else if (value.compare("vector-float32") == 0) + reg_info.format = eFormatVectorOfFloat32; + else if (value.compare("vector-uint128") == 0) + reg_info.format = eFormatVectorOfUInt128; + } + else if (name.compare("set") == 0) + { + set_name.SetCString(value.c_str()); + } + else if (name.compare("gcc") == 0) + { + reg_info.kinds[eRegisterKindGCC] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); + } + else if (name.compare("dwarf") == 0) + { + reg_info.kinds[eRegisterKindDWARF] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); + } + else if (name.compare("generic") == 0) + { + if (value.compare("pc") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if (value.compare("sp") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if (value.compare("fp") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if (value.compare("ra") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + else if (value.compare("flags") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + } + + assert (reg_info.byte_size != 0); + reg_offset += reg_info.byte_size; + m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); + } + } + else + { + packet_type = StringExtractorGDBRemote::eError; + } + } + + if (reg_num == 0) + { + // We didn't get anything. See if we are debugging ARM and fill with + // a hard coded register set until we can get an updated debugserver + // down on the devices. + ArchSpec arm_arch ("arm"); + if (GetTarget().GetArchitecture() == arm_arch) + m_register_info.HardcodeARMRegisters(); + } + m_register_info.Finalize (); +} + +Error +ProcessGDBRemote::WillLaunch (Module* module) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillAttach (lldb::pid_t pid) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillAttach (const char *process_name, bool wait_for_launch) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillLaunchOrAttach () +{ + Error error; + // TODO: this is hardcoded for macosx right now. We need this to be more dynamic + m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.macosx-dyld")); + + if (m_dynamic_loader_ap.get() == NULL) + error.SetErrorString("unable to find the dynamic loader named 'dynamic-loader.macosx-dyld'"); + m_stdio_communication.Clear (); + + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessGDBRemote::DoLaunch +( + Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ + // ::LogSetBitMask (GDBR_LOG_DEFAULT); + // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); + // ::LogSetLogFile ("/dev/stdout"); + Error error; + + ObjectFile * object_file = module->GetObjectFile(); + if (object_file) + { + ArchSpec inferior_arch(module->GetArchitecture()); + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + + bool start_debugserver_with_inferior_args = false; + if (start_debugserver_with_inferior_args) + { + // We want to launch debugserver with the inferior program and its + // arguments on the command line. We should only do this if we + // the GDB server we are talking to doesn't support the 'A' packet. + error = StartDebugserverProcess (host_port, + argv, + envp, + NULL, //stdin_path, + LLDB_INVALID_PROCESS_ID, + NULL, false, + inferior_arch); + if (error.Fail()) + return error; + + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout)); + } + } + else + { + error = StartDebugserverProcess (host_port, + NULL, + NULL, + NULL, //stdin_path, + LLDB_INVALID_PROCESS_ID, + NULL, false, + inferior_arch); + if (error.Fail()) + return error; + + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + // Send the environment and the program + arguments after we connect + if (envp) + { + const char *env_entry; + for (int i=0; (env_entry = envp[i]); ++i) + { + if (m_gdb_comm.SendEnvironmentPacket(env_entry, m_packet_timeout) != 0) + break; + } + } + + const uint32_t arg_timeout_seconds = 10; + int arg_packet_err = m_gdb_comm.SendArgumentsPacket (argv, arg_timeout_seconds); + if (arg_packet_err == 0) + { + std::string error_str; + if (m_gdb_comm.GetLaunchSuccess (m_packet_timeout, error_str)) + { + SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout)); + } + else + { + error.SetErrorString (error_str.c_str()); + } + } + else + { + error.SetErrorStringWithFormat("'A' packet returned an error: %i.\n", arg_packet_err); + } + + SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout)); + } + } + + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + KillDebugserverProcess (); + return error; + } + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, response, m_packet_timeout, false)) + SetPrivateState (SetThreadStopInfo (response)); + + } + else + { + // Set our user ID to an invalid process ID. + SetID(LLDB_INVALID_PROCESS_ID); + error.SetErrorStringWithFormat("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString()); + } + + // Return the process ID we have + return error; +} + + +Error +ProcessGDBRemote::ConnectToDebugserver (const char *host_port) +{ + Error error; + // Sleep and wait a bit for debugserver to start to listen... + std::auto_ptr conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + std::string connect_url("connect://"); + connect_url.append (host_port); + const uint32_t max_retry_count = 50; + uint32_t retry_count = 0; + while (!m_gdb_comm.IsConnected()) + { + if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) + { + m_gdb_comm.SetConnection (conn_ap.release()); + break; + } + retry_count++; + + if (retry_count >= max_retry_count) + break; + + usleep (100000); + } + } + + if (!m_gdb_comm.IsConnected()) + { + if (error.Success()) + error.SetErrorString("not connected to remote gdb server"); + return error; + } + + m_gdb_comm.SetAckMode (true); + if (m_gdb_comm.StartReadThread(&error)) + { + // Send an initial ack + m_gdb_comm.SendAck('+'); + + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + m_debugserver_monitor = Host::StartMonitoringChildProcess (MonitorDebugserverProcess, + (void*)(intptr_t)GetID(), // Pass the inferior pid in the thread argument (which is a void *) + m_debugserver_pid, + false); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("QStartNoAckMode", response, 1, false)) + { + if (response.IsOKPacket()) + m_gdb_comm.SetAckMode (false); + } + + BuildDynamicRegisterInfo (); + } + return error; +} + +void +ProcessGDBRemote::DidLaunchOrAttach () +{ + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::DidLaunch()"); + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + m_dynamic_loader_ap.reset(); + } + else + { + m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; + + Module * exe_module = GetTarget().GetExecutableModule ().get(); + assert(exe_module); + + m_arch_spec = exe_module->GetArchitecture(); + + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + assert(exe_objfile); + + m_byte_order = exe_objfile->GetByteOrder(); + assert (m_byte_order != eByteOrderInvalid); + + StreamString strm; + + ArchSpec inferior_arch; + // See if the GDB server supports the qHostInfo information + const char *vendor = m_gdb_comm.GetVendorString().AsCString(); + const char *os_type = m_gdb_comm.GetOSString().AsCString(); + + if (m_arch_spec.IsValid() && m_arch_spec == ArchSpec ("arm")) + { + // For ARM we can't trust the arch of the process as it could + // have an armv6 object file, but be running on armv7 kernel. + inferior_arch = m_gdb_comm.GetHostArchitecture(); + } + + if (!inferior_arch.IsValid()) + inferior_arch = m_arch_spec; + + if (vendor == NULL) + vendor = Host::GetVendorString().AsCString("apple"); + + if (os_type == NULL) + os_type = Host::GetOSString().AsCString("darwin"); + + strm.Printf ("%s-%s-%s", inferior_arch.AsCString(), vendor, os_type); + + std::transform (strm.GetString().begin(), + strm.GetString().end(), + strm.GetString().begin(), + ::tolower); + + m_target_triple.SetCString(strm.GetString().c_str()); + } +} + +void +ProcessGDBRemote::DidLaunch () +{ + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidLaunch(); +} + +Error +ProcessGDBRemote::DoAttach (pid_t attach_pid) +{ + Error error; + // Clear out and clean up from any current state + Clear(); + // HACK: require arch be set correctly at the target level until we can + // figure out a good way to determine the arch of what we are attaching to + m_arch_spec = m_target.GetArchitecture(); + + //Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + SetPrivateState (eStateAttaching); + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + error = StartDebugserverProcess (host_port, + NULL, + NULL, + NULL, + LLDB_INVALID_PROCESS_ID, + NULL, false, + m_arch_spec); + + if (error.Fail()) + { + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + + SetExitStatus (-1, error_string); + } + else + { + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%x", attach_pid); + StringExtractorGDBRemote response; + StateType stop_state = m_gdb_comm.SendContinuePacketAndWaitForResponse (this, + packet, + packet_len, + response); + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + SetID (attach_pid); + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + SetPrivateState (stop_state); + break; + + case eStateExited: + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + response.SetFilePos(1); + SetExitStatus(response.GetHexU8(), NULL); + break; + + default: + SetExitStatus(-1, "unable to attach to process"); + break; + } + + } + } + } + + lldb::pid_t pid = GetID(); + if (pid == LLDB_INVALID_PROCESS_ID) + { + KillDebugserverProcess(); + } + return error; +} + +size_t +ProcessGDBRemote::AttachInputReaderCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + if (notification == eInputReaderGotToken) + { + ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)baton; + if (gdb_process->m_waiting_for_attach) + gdb_process->m_waiting_for_attach = false; + reader->SetIsDone(true); + return 1; + } + return 0; +} + +Error +ProcessGDBRemote::DoAttach (const char *process_name, bool wait_for_launch) +{ + Error error; + // Clear out and clean up from any current state + Clear(); + // HACK: require arch be set correctly at the target level until we can + // figure out a good way to determine the arch of what we are attaching to + m_arch_spec = m_target.GetArchitecture(); + + //Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (process_name && process_name[0]) + { + + SetPrivateState (eStateAttaching); + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + error = StartDebugserverProcess (host_port, + NULL, + NULL, + NULL, + LLDB_INVALID_PROCESS_ID, + NULL, false, + m_arch_spec); + if (error.Fail()) + { + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + + SetExitStatus (-1, error_string); + } + else + { + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + StreamString packet; + + packet.PutCString("vAttach"); + if (wait_for_launch) + packet.PutCString("Wait"); + packet.PutChar(';'); + packet.PutBytesAsRawHex8(process_name, strlen(process_name), eByteOrderHost, eByteOrderHost); + StringExtractorGDBRemote response; + StateType stop_state = m_gdb_comm.SendContinuePacketAndWaitForResponse (this, + packet.GetData(), + packet.GetSize(), + response); + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + SetID (m_gdb_comm.GetCurrentProcessID(m_packet_timeout)); + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + SetPrivateState (stop_state); + break; + + case eStateExited: + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + response.SetFilePos(1); + SetExitStatus(response.GetHexU8(), NULL); + break; + + default: + SetExitStatus(-1, "unable to attach to process"); + break; + } + } + } + } + + lldb::pid_t pid = GetID(); + if (pid == LLDB_INVALID_PROCESS_ID) + { + KillDebugserverProcess(); + } + return error; +} + +// +// if (wait_for_launch) +// { +// InputReaderSP reader_sp (new InputReader()); +// StreamString instructions; +// instructions.Printf("Hit any key to cancel waiting for '%s' to launch...", process_name); +// error = reader_sp->Initialize (AttachInputReaderCallback, // callback +// this, // baton +// eInputReaderGranularityByte, +// NULL, // End token +// false); +// +// StringExtractorGDBRemote response; +// m_waiting_for_attach = true; +// FILE *reader_out_fh = reader_sp->GetOutputFileHandle(); +// while (m_waiting_for_attach) +// { +// // Wait for one second for the stop reply packet +// if (m_gdb_comm.WaitForPacket(response, 1)) +// { +// // Got some sort of packet, see if it is the stop reply packet? +// char ch = response.GetChar(0); +// if (ch == 'T') +// { +// m_waiting_for_attach = false; +// } +// } +// else +// { +// // Put a period character every second +// fputc('.', reader_out_fh); +// } +// } +// } +// } +// return GetID(); +//} + +void +ProcessGDBRemote::DidAttach () +{ + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidAttach(); +} + +Error +ProcessGDBRemote::WillResume () +{ + m_continue_packet.Clear(); + // Start the continue packet we will use to run the target. Each thread + // will append what it is supposed to be doing to this packet when the + // ThreadList::WillResume() is called. If a thread it supposed + // to stay stopped, then don't append anything to this string. + m_continue_packet.Printf("vCont"); + return Error(); +} + +Error +ProcessGDBRemote::DoResume () +{ + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::Resume()"); + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (m_continue_packet.GetData(), m_continue_packet.GetSize())); + return Error(); +} + +size_t +ProcessGDBRemote::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site) +{ + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + + switch (m_arch_spec.GetCPUType()) + { + case CPU_TYPE_ARM: + // TODO: fill this in for ARM. We need to dig up the symbol for + // the address in the breakpoint locaiton and figure out if it is + // an ARM or Thumb breakpoint. + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + break; + + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + break; + + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + break; + + default: + assert(!"Unhandled architecture in ProcessGDBRemote::GetSoftwareBreakpointTrapOpcode()"); + return 0; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + return 0; +} + +uint32_t +ProcessGDBRemote::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD); + if (log && log->GetMask().IsSet(GDBR_LOG_VERBOSE)) + log->Printf ("ProcessGDBRemote::%s (pid = %i)", __FUNCTION__, GetID()); + + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) + { + // Update the thread list's stop id immediately so we don't recurse into this function. + ThreadList curr_thread_list (this); + curr_thread_list.SetStopID(stop_id); + + Error err; + StringExtractorGDBRemote response; + for (m_gdb_comm.SendPacketAndWaitForResponse("qfThreadInfo", response, 1, false); + response.IsNormalPacket(); + m_gdb_comm.SendPacketAndWaitForResponse("qsThreadInfo", response, 1, false)) + { + char ch = response.GetChar(); + if (ch == 'l') + break; + if (ch == 'm') + { + do + { + tid_t tid = response.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); + + if (tid != LLDB_INVALID_THREAD_ID) + { + ThreadSP thread_sp (GetThreadList().FindThreadByID (tid, false)); + if (thread_sp) + thread_sp->GetRegisterContext()->Invalidate(); + else + thread_sp.reset (new ThreadGDBRemote (*this, tid)); + curr_thread_list.AddThread(thread_sp); + } + + ch = response.GetChar(); + } while (ch == ','); + } + } + + m_thread_list = curr_thread_list; + + SetThreadStopInfo (m_last_stop_packet); + } + return GetThreadList().GetSize(false); +} + + +StateType +ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) +{ + const char stop_type = stop_packet.GetChar(); + switch (stop_type) + { + case 'T': + case 'S': + { + // Stop with signal and thread info + const uint8_t signo = stop_packet.GetHexU8(); + std::string name; + std::string value; + std::string thread_name; + uint32_t exc_type = 0; + std::vector exc_data; + uint32_t tid = LLDB_INVALID_THREAD_ID; + addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + uint32_t exc_data_count = 0; + while (stop_packet.GetNameColonValue(name, value)) + { + if (name.compare("metype") == 0) + { + // exception type in big endian hex + exc_type = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("mecount") == 0) + { + // exception count in big endian hex + exc_data_count = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("medata") == 0) + { + // exception data in big endian hex + exc_data.push_back(Args::StringToUInt64 (value.c_str(), 0, 16)); + } + else if (name.compare("thread") == 0) + { + // thread in big endian hex + tid = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("name") == 0) + { + thread_name.swap (value); + } + else if (name.compare("dispatchqaddr") == 0) + { + thread_dispatch_qaddr = Args::StringToUInt64 (value.c_str(), 0, 16); + } + } + ThreadSP thread_sp (m_thread_list.FindThreadByID(tid, false)); + + if (thread_sp) + { + ThreadGDBRemote *gdb_thread = static_cast (thread_sp.get()); + + gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); + gdb_thread->SetName (thread_name.empty() ? thread_name.c_str() : NULL); + Thread::StopInfo& stop_info = gdb_thread->GetStopInfoRef(); + gdb_thread->SetStopInfoStopID (GetStopID()); + if (exc_type != 0) + { + if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL) + { + stop_info.SetStopReasonWithSignal(exc_data[1]); + } +#if defined (MACH_EXC_DATA0_SOFTWARE_BREAKPOINT) + else if (exc_type == EXC_BREAKPOINT && exc_data[0] == MACH_EXC_DATA0_SOFTWARE_BREAKPOINT) + { + addr_t pc = gdb_thread->GetRegisterContext()->GetPC(); + user_id_t break_id = GetBreakpointSiteList().FindIDByAddress(pc); + if (break_id == LLDB_INVALID_BREAK_ID) + { + //log->Printf("got EXC_BREAKPOINT at 0x%llx but didn't find a breakpoint site.\n", pc); + stop_info.SetStopReasonWithException(exc_type, exc_data.size()); + for (uint32_t i=0; iGetArchitecture(); + } + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); + + // Discover new threads: + UpdateThreadListIfNeeded (); +} + +Error +ProcessGDBRemote::DoHalt () +{ + Error error; + if (m_gdb_comm.IsRunning()) + { + bool timed_out = false; + if (!m_gdb_comm.SendInterrupt (2, &timed_out)) + { + if (timed_out) + error.SetErrorString("timed out sending interrupt packet"); + else + error.SetErrorString("unknown error sending interrupt packet"); + } + } + return error; +} + +Error +ProcessGDBRemote::WillDetach () +{ + Error error; + const StateType state = m_private_state.GetValue(); + + if (IsRunning(state)) + error.SetErrorString("Process must be stopped in order to detach."); + + return error; +} + + +Error +ProcessGDBRemote::DoDestroy () +{ + Error error; + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy()"); + + // Interrupt if our inferior is running... + m_gdb_comm.SendInterrupt (1); + DisableAllBreakpointSites (); + SetExitStatus(-1, "process killed"); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, 2, false)) + { + if (log) + { + if (response.IsOKPacket()) + log->Printf ("ProcessGDBRemote::DoDestroy() kill was successful"); + else + log->Printf ("ProcessGDBRemote::DoDestroy() kill failed: %s", response.GetStringRef().c_str()); + } + } + + StopAsyncThread (); + m_gdb_comm.StopReadThread(); + KillDebugserverProcess (); + return error; +} + +ByteOrder +ProcessGDBRemote::GetByteOrder () const +{ + return m_byte_order; +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessGDBRemote::IsAlive () +{ + return m_gdb_comm.IsConnected(); +} + +addr_t +ProcessGDBRemote::GetImageInfoAddress() +{ + if (!m_gdb_comm.IsRunning()) + { + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, 2, false)) + { + if (response.IsNormalPacket()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } + } + return LLDB_INVALID_ADDRESS; +} + +DynamicLoader * +ProcessGDBRemote::GetDynamicLoader() +{ + return m_dynamic_loader_ap.get(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + if (size > m_max_memory_size) + { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = m_max_memory_size; + } + + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "m%llx,%zx", (uint64_t)addr, size); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true)) + { + if (response.IsNormalPacket()) + { + error.Clear(); + return response.GetHexBytes(buf, size, '\xdd'); + } + else if (response.IsErrorPacket()) + error.SetErrorStringWithFormat("gdb remote returned an error: %s", response.GetStringRef().c_str()); + else if (response.IsUnsupportedPacket()) + error.SetErrorStringWithFormat("'%s' packet unsupported", packet); + else + error.SetErrorStringWithFormat("unexpected response to '%s': '%s'", packet, response.GetStringRef().c_str()); + } + else + { + error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet); + } + return 0; +} + +size_t +ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + StreamString packet; + packet.Printf("M%llx,%zx:", addr, size); + packet.PutBytesAsRawHex8(buf, size, eByteOrderHost, eByteOrderHost); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, 2, true)) + { + if (response.IsOKPacket()) + { + error.Clear(); + return size; + } + else if (response.IsErrorPacket()) + error.SetErrorStringWithFormat("gdb remote returned an error: %s", response.GetStringRef().c_str()); + else if (response.IsUnsupportedPacket()) + error.SetErrorStringWithFormat("'%s' packet unsupported", packet.GetString().c_str()); + else + error.SetErrorStringWithFormat("unexpected response to '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str()); + } + else + { + error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet.GetString().c_str()); + } + return 0; +} + +lldb::addr_t +ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) +{ + addr_t allocated_addr = m_gdb_comm.AllocateMemory (size, permissions, m_packet_timeout); + if (allocated_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %u", size, permissions); + else + error.Clear(); + return allocated_addr; +} + +Error +ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) +{ + Error error; + if (!m_gdb_comm.DeallocateMemory (addr, m_packet_timeout)) + error.SetErrorStringWithFormat("unable to deallocate memory at 0x%llx", addr); + return error; +} + + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ + +size_t +ProcessGDBRemote::GetSTDOUT (char *buf, size_t buf_size, Error &error) +{ + Mutex::Locker locker(m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + + //ResetEventBits(eBroadcastBitSTDOUT); + } + } + return bytes_available; +} + +size_t +ProcessGDBRemote::GetSTDERR (char *buf, size_t buf_size, Error &error) +{ + // Can we get STDERR through the remote protocol? + return 0; +} + +size_t +ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) +{ + if (m_stdio_communication.IsConnected()) + { + ConnectionStatus status; + m_stdio_communication.Write(src, src_len, status, NULL); + } + return 0; +} + +Error +ProcessGDBRemote::EnableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS); + user_id_t site_id = bp_site->GetID(); + const addr_t addr = bp_site->GetLoadAddress(); + if (log) + log->Printf ("ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx", site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx -- SUCCESS (already enabled)", site_id, (uint64_t)addr); + return error; + } + else + { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); + + if (bp_site->HardwarePreferred()) + { + // Try and set hardware breakpoint, and if that fails, fall through + // and set a software breakpoint? + } + + if (m_z0_supported) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "Z0,%llx,%zx", addr, bp_op_size); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true)) + { + if (response.IsUnsupportedPacket()) + { + // Disable z packet support and try again + m_z0_supported = 0; + return EnableBreakpoint (bp_site); + } + else if (response.IsOKPacket()) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eExternal); + return error; + } + else + { + uint8_t error_byte = response.GetError(); + if (error_byte) + error.SetErrorStringWithFormat("%x packet failed with error: %i (0x%2.2x).\n", packet, error_byte, error_byte); + } + } + } + else + { + return EnableSoftwareBreakpoint (bp_site); + } + } + + if (log) + { + const char *err_string = error.AsCString(); + log->Printf ("ProcessGDBRemote::EnableBreakpoint() error for breakpoint at 0x%8.8llx: %s", + bp_site->GetLoadAddress(), + err_string ? err_string : "NULL"); + } + // We shouldn't reach here on a successful breakpoint enable... + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::DisableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + addr_t addr = bp_site->GetLoadAddress(); + user_id_t site_id = bp_site->GetID(); + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); + + if (bp_site->IsHardware()) + { + // TODO: disable hardware breakpoint... + } + else + { + if (m_z0_supported) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "z0,%llx,%zx", addr, bp_op_size); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true)) + { + if (response.IsUnsupportedPacket()) + { + error.SetErrorString("Breakpoint site was set with Z packet, yet remote debugserver states z packets are not supported."); + } + else if (response.IsOKPacket()) + { + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS", site_id, (uint64_t)addr); + bp_site->SetEnabled(false); + return error; + } + else + { + uint8_t error_byte = response.GetError(); + if (error_byte) + error.SetErrorStringWithFormat("%x packet failed with error: %i (0x%2.2x).\n", packet, error_byte, error_byte); + } + } + } + else + { + return DisableSoftwareBreakpoint (bp_site); + } + } + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already disabled)", site_id, (uint64_t)addr); + return error; + } + + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::EnableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + addr_t addr = wp->GetLoadAddress(); + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS); + if (log) + log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %d)", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return error; + } + else + { + // Pass down an appropriate z/Z packet... + error.SetErrorString("watchpoints not supported"); + } + } + else + { + error.SetErrorString("Watchpoint location argument was NULL."); + } + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::DisableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS); + + addr_t addr = wp->GetLoadAddress(); + if (log) + log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr); + + if (wp->IsHardware()) + { + // Pass down an appropriate z/Z packet... + error.SetErrorString("watchpoints not supported"); + } + // TODO: clear software watchpoints if we implement them + } + else + { + error.SetErrorString("Watchpoint location argument was NULL."); + } + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +void +ProcessGDBRemote::Clear() +{ + m_flags = 0; + m_thread_list.Clear(); + { + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.clear(); + } + DestoryLibUnwindAddressSpace(); +} + +Error +ProcessGDBRemote::DoSignal (int signo) +{ + Error error; + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo); + + if (!m_gdb_comm.SendAsyncSignal (signo)) + error.SetErrorStringWithFormat("failed to send signal %i", signo); + return error; +} + + +Error +ProcessGDBRemote::DoDetach() +{ + Error error; + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::DoDetach()"); + + // if (DoSIGSTOP (true)) + // { + // CloseChildFileDescriptors (); + // + // // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP + // // exception). + // { + // Mutex::Locker locker(m_exception_messages_mutex); + // ReplyToAllExceptions(); + // } + // + // // Shut down the exception thread and cleanup our exception remappings + // Task().ShutDownExceptionThread(); + // + // pid_t pid = GetID(); + // + // // Detach from our process while we are stopped. + // errno = 0; + // + // // Detach from our process + // ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + // + // error.SetErrorToErrno(); + // + // if (log || error.Fail()) + // error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + // + // // Resume our task + // Task().Resume(); + // + // // NULL our task out as we have already retored all exception ports + // Task().Clear(); + // + // // Clear out any notion of the process we once were + // Clear(); + // + // SetPrivateState (eStateDetached); + // return true; + // } + return error; +} + +void +ProcessGDBRemote::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len) +{ + ProcessGDBRemote *process = (ProcessGDBRemote *)baton; + process->AppendSTDOUT(static_cast(src), src_len); +} + +void +ProcessGDBRemote::AppendSTDOUT (const char* s, size_t len) +{ + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::%s (<%d> %s) ...", __FUNCTION__, len, s); + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.append(s, len); + + // FIXME: Make a real data object for this and put it out. + BroadcastEventIfUnique (eBroadcastBitSTDOUT); +} + + +Error +ProcessGDBRemote::StartDebugserverProcess +( + const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") + char const *inferior_argv[], // Arguments for the inferior program including the path to the inferior itself as the first argument + char const *inferior_envp[], // Environment to pass along to the inferior program + char const *stdio_path, + lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, and attach_pid != LLDB_INVALID_PROCESS_ID then attach to this attach_pid + const char *attach_name, // Wait for the next process to launch whose basename matches "attach_name" + bool wait_for_launch, // Wait for the process named "attach_name" to launch + ArchSpec& inferior_arch // The arch of the inferior that we will launch +) +{ + Error error; + if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) + { + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + FileSpec debugserver_file_spec; + char debugserver_path[PATH_MAX]; + + // Always check to see if we have an environment override for the path + // to the debugserver to use and use it if we do. + const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); + if (env_debugserver_path) + debugserver_file_spec.SetFile (env_debugserver_path); + else + debugserver_file_spec = g_debugserver_file_spec; + bool debugserver_exists = debugserver_file_spec.Exists(); + if (!debugserver_exists) + { + // The debugserver binary is in the LLDB.framework/Resources + // directory. + FileSpec framework_file_spec (Host::GetModuleFileSpecForHostAddress ((void *)lldb_private::Initialize)); + const char *framework_dir = framework_file_spec.GetDirectory().AsCString(); + const char *lldb_framework = ::strstr (framework_dir, "/LLDB.framework"); + + if (lldb_framework) + { + int len = lldb_framework - framework_dir + strlen ("/LLDB.framework"); + ::snprintf (debugserver_path, + sizeof(debugserver_path), + "%.*s/Resources/%s", + len, + framework_dir, + DEBUGSERVER_BASENAME); + debugserver_file_spec.SetFile (debugserver_path); + debugserver_exists = debugserver_file_spec.Exists(); + } + + if (debugserver_exists) + { + g_debugserver_file_spec = debugserver_file_spec; + } + else + { + g_debugserver_file_spec.Clear(); + debugserver_file_spec.Clear(); + } + } + + if (debugserver_exists) + { + debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); + + m_stdio_communication.Clear(); + posix_spawnattr_t attr; + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + + Error local_err; // Errors that don't affect the spawning. + if (log) + log->Printf ("%s ( path='%s', argv=%p, envp=%p, arch=%s )", __FUNCTION__, debugserver_path, inferior_argv, inferior_envp, inferior_arch.AsCString()); + error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_init ( &attr )"); + if (error.Fail()) + return error;; + +#if !defined (__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu = inferior_arch.GetCPUType(); + if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE) + { + size_t ocount = 0; + error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount); + + if (error.Fail() != 0 || ocount != 1) + return error; + } + +#endif + + Args debugserver_args; + char arg_cstr[PATH_MAX]; + bool launch_process = true; + + if (inferior_argv == NULL && attach_pid != LLDB_INVALID_PROCESS_ID) + launch_process = false; + else if (attach_name) + launch_process = false; // Wait for a process whose basename matches that in inferior_argv[0] + + bool pass_stdio_path_to_debugserver = true; + lldb_utility::PseudoTerminal pty; + if (stdio_path == NULL) + { + pass_stdio_path_to_debugserver = false; + if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) + { + struct termios stdin_termios; + if (::tcgetattr (pty.GetMasterFileDescriptor(), &stdin_termios) == 0) + { + stdin_termios.c_lflag &= ~ECHO; // Turn off echoing + stdin_termios.c_lflag &= ~ICANON; // Get one char at a time + ::tcsetattr (pty.GetMasterFileDescriptor(), TCSANOW, &stdin_termios); + } + stdio_path = pty.GetSlaveName (NULL, 0); + } + } + + // Start args with "debugserver /file/path -r --" + debugserver_args.AppendArgument(debugserver_path); + debugserver_args.AppendArgument(debugserver_url); + debugserver_args.AppendArgument("--native-regs"); // use native registers, not the GDB registers + debugserver_args.AppendArgument("--setsid"); // make debugserver run in its own session so + // signals generated by special terminal key + // sequences (^C) don't affect debugserver + + // Only set the inferior + if (launch_process) + { + if (stdio_path && pass_stdio_path_to_debugserver) + { + debugserver_args.AppendArgument("-s"); // short for --stdio-path + StreamString strm; + strm.Printf("'%s'", stdio_path); + debugserver_args.AppendArgument(strm.GetData()); // path to file to have inferior open as it's STDIO + } + } + + const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); + if (env_debugserver_log_file) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); + debugserver_args.AppendArgument(arg_cstr); + } + + const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); + if (env_debugserver_log_flags) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); + debugserver_args.AppendArgument(arg_cstr); + } +// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); +// debugserver_args.AppendArgument("--log-flags=0x800e0e"); + + // Now append the program arguments + if (launch_process) + { + if (inferior_argv) + { + // Terminate the debugserver args so we can now append the inferior args + debugserver_args.AppendArgument("--"); + + for (int i = 0; inferior_argv[i] != NULL; ++i) + debugserver_args.AppendArgument (inferior_argv[i]); + } + else + { + // Will send environment entries with the 'QEnvironment:' packet + // Will send arguments with the 'A' packet + } + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); + debugserver_args.AppendArgument (arg_cstr); + } + else if (attach_name && attach_name[0]) + { + if (wait_for_launch) + debugserver_args.AppendArgument ("--waitfor"); + else + debugserver_args.AppendArgument ("--attach"); + debugserver_args.AppendArgument (attach_name); + } + + Error file_actions_err; + posix_spawn_file_actions_t file_actions; +#if DONT_CLOSE_DEBUGSERVER_STDIO + file_actions_err.SetErrorString ("Remove this after uncommenting the code block below."); +#else + file_actions_err.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + if (file_actions_err.Success()) + { + ::posix_spawn_file_actions_addclose (&file_actions, STDIN_FILENO); + ::posix_spawn_file_actions_addclose (&file_actions, STDOUT_FILENO); + ::posix_spawn_file_actions_addclose (&file_actions, STDERR_FILENO); + } +#endif + + if (log) + { + StreamString strm; + debugserver_args.Dump (&strm); + log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData()); + } + + error.SetError(::posix_spawnp (&m_debugserver_pid, + debugserver_path, + file_actions_err.Success() ? &file_actions : NULL, + &attr, + debugserver_args.GetArgumentVector(), + (char * const*)inferior_envp), + eErrorTypePOSIX); + + if (file_actions_err.Success()) + ::posix_spawn_file_actions_destroy (&file_actions); + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (error.Fail()) + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", m_debugserver_pid, debugserver_path, NULL, &attr, inferior_argv, inferior_envp); + +// if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) +// { +// std::auto_ptr conn_ap(new ConnectionFileDescriptor (pty.ReleaseMasterFileDescriptor(), true)); +// if (conn_ap.get()) +// { +// m_stdio_communication.SetConnection(conn_ap.release()); +// if (m_stdio_communication.IsConnected()) +// { +// m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this); +// m_stdio_communication.StartReadThread(); +// } +// } +// } + } + else + { + error.SetErrorStringWithFormat ("Unable to locate " DEBUGSERVER_BASENAME ".\n"); + } + + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + StartAsyncThread (); + } + return error; +} + +bool +ProcessGDBRemote::MonitorDebugserverProcess +( + void *callback_baton, + lldb::pid_t debugserver_pid, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero +) +{ + // We pass in the ProcessGDBRemote inferior process it and name it + // "gdb_remote_pid". The process ID is passed in the "callback_baton" + // pointer value itself, thus we need the double cast... + + // "debugserver_pid" argument passed in is the process ID for + // debugserver that we are tracking... + + lldb::pid_t gdb_remote_pid = (lldb::pid_t)(intptr_t)callback_baton; + TargetSP target_sp(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcessID (gdb_remote_pid)); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + { + // Sleep for a half a second to make sure our inferior process has + // time to set its exit status before we set it incorrectly when + // both the debugserver and the inferior process shut down. + usleep (500000); + // If our process hasn't yet exited, debugserver might have died. + // If the process did exit, the we are reaping it. + if (process_sp->GetState() != eStateExited) + { + char error_str[1024]; + if (signo) + { + const char *signal_cstr = process_sp->GetUnixSignals().GetSignalAsCString (signo); + if (signal_cstr) + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); + else + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); + } + else + { + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); + } + + process_sp->SetExitStatus (-1, error_str); + } + else + { + ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)process_sp.get(); + // Debugserver has exited we need to let our ProcessGDBRemote + // know that it no longer has a debugserver instance + gdb_process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + // We are returning true to this function below, so we can + // forget about the monitor handle. + gdb_process->m_debugserver_monitor = 0; + } + } + } + return true; +} + +void +ProcessGDBRemote::KillDebugserverProcess () +{ + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + ::kill (m_debugserver_pid, SIGINT); + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + } +} + +void +ProcessGDBRemote::Initialize() +{ + static bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessGDBRemoteLog::DisableLog, + ProcessGDBRemoteLog::EnableLog, + ProcessGDBRemoteLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks); + } +} + +bool +ProcessGDBRemote::SetCurrentGDBRemoteThread (int tid) +{ + if (m_curr_tid == tid) + return true; + + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof(packet), "Hg%x", tid); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, false)) + { + if (response.IsOKPacket()) + { + m_curr_tid = tid; + return true; + } + } + return false; +} + +bool +ProcessGDBRemote::SetCurrentGDBRemoteThreadForRun (int tid) +{ + if (m_curr_tid_run == tid) + return true; + + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof(packet), "Hg%x", tid); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, false)) + { + if (response.IsOKPacket()) + { + m_curr_tid_run = tid; + return true; + } + } + return false; +} + +void +ProcessGDBRemote::ResetGDBRemoteState () +{ + // Reset and GDB remote state + m_curr_tid = LLDB_INVALID_THREAD_ID; + m_curr_tid_run = LLDB_INVALID_THREAD_ID; + m_z0_supported = 1; +} + + +bool +ProcessGDBRemote::StartAsyncThread () +{ + ResetGDBRemoteState (); + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + + if (log) + log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); + + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + m_async_thread = Host::ThreadCreate ("", ProcessGDBRemote::AsyncThread, this, NULL); + return m_async_thread != LLDB_INVALID_HOST_THREAD; +} + +void +ProcessGDBRemote::StopAsyncThread () +{ + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + + if (log) + log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // Stop the stdio thread + if (m_async_thread != LLDB_INVALID_HOST_THREAD) + { + Host::ThreadJoin (m_async_thread, NULL, NULL); + } +} + + +void * +ProcessGDBRemote::AsyncThread (void *arg) +{ + ProcessGDBRemote *process = (ProcessGDBRemote*) arg; + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID()); + + Listener listener ("ProcessGDBRemote::AsyncThread"); + EventSP event_sp; + const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | + eBroadcastBitAsyncThreadShouldExit; + + if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) + { + bool done = false; + while (!done) + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); + if (listener.WaitForEvent (NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); + + if (continue_packet) + { + const char *continue_cstr = (const char *)continue_packet->GetBytes (); + const size_t continue_cstr_len = continue_packet->GetByteSize (); + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); + + process->SetPrivateState(eStateRunning); + StringExtractorGDBRemote response; + StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); + + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + process->m_last_stop_packet = response; + process->m_last_stop_packet.SetFilePos (0); + process->SetPrivateState (stop_state); + break; + + case eStateExited: + process->m_last_stop_packet = response; + process->m_last_stop_packet.SetFilePos (0); + response.SetFilePos(1); + process->SetExitStatus(response.GetHexU8(), NULL); + done = true; + break; + + case eStateInvalid: + break; + + default: + process->SetPrivateState (stop_state); + break; + } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); + done = true; + break; + + default: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); + done = true; + break; + } + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID()); + + process->m_async_thread = LLDB_INVALID_HOST_THREAD; + return NULL; +} + +lldb_private::unw_addr_space_t +ProcessGDBRemote::GetLibUnwindAddressSpace () +{ + unw_targettype_t target_type = UNW_TARGET_UNSPECIFIED; + if (m_target.GetArchitecture().GetCPUType() == CPU_TYPE_I386) + target_type = UNW_TARGET_I386; + if (m_target.GetArchitecture().GetCPUType() == CPU_TYPE_X86_64) + target_type = UNW_TARGET_X86_64; + + if (m_libunwind_addr_space) + { + if (m_libunwind_target_type != target_type) + DestoryLibUnwindAddressSpace(); + else + return m_libunwind_addr_space; + } + unw_accessors_t callbacks = get_macosx_libunwind_callbacks (); + m_libunwind_addr_space = unw_create_addr_space (&callbacks, target_type); + if (m_libunwind_addr_space) + m_libunwind_target_type = target_type; + else + m_libunwind_target_type = UNW_TARGET_UNSPECIFIED; + return m_libunwind_addr_space; +} + +void +ProcessGDBRemote::DestoryLibUnwindAddressSpace () +{ + if (m_libunwind_addr_space) + { + unw_destroy_addr_space (m_libunwind_addr_space); + m_libunwind_addr_space = NULL; + } + m_libunwind_target_type = UNW_TARGET_UNSPECIFIED; +} + + +const char * +ProcessGDBRemote::GetDispatchQueueNameForThread +( + addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name +) +{ + dispatch_queue_name.clear(); + if (thread_dispatch_qaddr != 0 && thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + { + // Cache the dispatch_queue_offsets_addr value so we don't always have + // to look it up + if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + { + ModuleSP module_sp(GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib"))); + if (module_sp.get() == NULL) + return NULL; + + const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData); + if (dispatch_queue_offsets_symbol) + m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(this); + + if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + return NULL; + } + + uint8_t memory_buffer[8]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), GetByteOrder(), GetAddressByteSize()); + + // Excerpt from src/queue_private.h + struct dispatch_queue_offsets_s + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + } dispatch_queue_offsets; + + + Error error; + if (ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets)) + { + uint32_t data_offset = 0; + if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) + { + if (ReadMemory (thread_dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) + { + data_offset = 0; + lldb::addr_t queue_addr = data.GetAddress(&data_offset); + lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; + dispatch_queue_name.resize(dispatch_queue_offsets.dqo_label_size, '\0'); + size_t bytes_read = ReadMemory (label_addr, &dispatch_queue_name[0], dispatch_queue_offsets.dqo_label_size, error); + if (bytes_read < dispatch_queue_offsets.dqo_label_size) + dispatch_queue_name.erase (bytes_read); + } + } + } + } + if (dispatch_queue_name.empty()) + return NULL; + return dispatch_queue_name.c_str(); +} + diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h new file mode 100644 index 000000000000..cd5bab0194f9 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -0,0 +1,404 @@ +//===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessGDBRemote_h_ +#define liblldb_ProcessGDBRemote_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "GDBRemoteCommunication.h" +#include "StringExtractor.h" +#include "GDBRemoteRegisterContext.h" +#include "libunwind.h" + +class ThreadGDBRemote; + +class ProcessGDBRemote : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static Process* + CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessGDBRemote(lldb_private::Target& target, lldb_private::Listener &listener); + + virtual + ~ProcessGDBRemote(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path); // Can be NULL + + virtual void + DidLaunch (); + + virtual lldb_private::Error + WillAttach (lldb::pid_t pid); + + virtual lldb_private::Error + WillAttach (const char *process_name, bool wait_for_launch); + + lldb_private::Error + WillLaunchOrAttach (); + + virtual lldb_private::Error + DoAttach (lldb::pid_t pid); + + virtual lldb_private::Error + DoAttach (const char *process_name, bool wait_for_launch); + + virtual void + DidAttach (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + WillResume (); + + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (); + + virtual lldb_private::Error + WillDetach (); + + virtual lldb_private::Error + DoDetach (); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + virtual lldb::addr_t + GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t + GetSTDOUT (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + GetSTDERR (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpoint (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpoint (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual lldb_private::DynamicLoader * + GetDynamicLoader (); + +protected: + friend class ThreadGDBRemote; + friend class GDBRemoteCommunication; + friend class GDBRemoteRegisterContext; + + bool + SetCurrentGDBRemoteThread (int tid); + + bool + SetCurrentGDBRemoteThreadForRun (int tid); + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + bool + ProcessIDIsValid ( ) const; + + static void + STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + void + AppendSTDOUT (const char* s, size_t len); + + lldb_private::ArchSpec& + GetArchSpec() + { + return m_arch_spec; + } + const lldb_private::ArchSpec& + GetArchSpec() const + { + return m_arch_spec; + } + + void + Clear ( ); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + + uint32_t + UpdateThreadListIfNeeded (); + + lldb_private::Error + StartDebugserverProcess (const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") + char const *inferior_argv[], + char const *inferior_envp[], + const char *stdin_path, + lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, then attach to this pid + const char *attach_pid_name, // Wait for the next process to launch whose basename matches "attach_wait_name" + bool wait_for_launch, // Wait for the process named "attach_wait_name" to launch + lldb_private::ArchSpec& arch_spec); + + void + KillDebugserverProcess (); + + void + BuildDynamicRegisterInfo (); + + GDBRemoteCommunication & + GetGDBRemote() + { + return m_gdb_comm; + } + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1) + }; + + + std::auto_ptr m_dynamic_loader_ap; + lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) + lldb_private::Communication m_stdio_communication; + lldb_private::Mutex m_stdio_mutex; // Multithreaded protection for stdio + std::string m_stdout_data; + lldb_private::ArchSpec m_arch_spec; + lldb::ByteOrder m_byte_order; + GDBRemoteCommunication m_gdb_comm; + lldb::pid_t m_debugserver_pid; + uint32_t m_debugserver_monitor; + StringExtractor m_last_stop_packet; + GDBRemoteDynamicRegisterInfo m_register_info; + lldb_private::Broadcaster m_async_broadcaster; + lldb::thread_t m_async_thread; + // Current GDB remote state. Any members added here need to be reset to + // proper default values in ResetGDBRemoteState (). + lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations + lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc + uint32_t m_z0_supported:1; // Set to non-zero if Z0 and z0 packets are supported + lldb_private::StreamString m_continue_packet; + lldb::addr_t m_dispatch_queue_offsets_addr; + uint32_t m_packet_timeout; + size_t m_max_memory_size; // The maximum number of bytes to read/write when reading and writing memory + lldb_private::unw_targettype_t m_libunwind_target_type; + lldb_private::unw_addr_space_t m_libunwind_addr_space; // libunwind address space object for this process. + bool m_waiting_for_attach; + + void + ResetGDBRemoteState (); + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + + static bool + MonitorDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + int signo, // Zero for no signal + int exit_status); // Exit value of process if signal is zero + + lldb::StateType + SetThreadStopInfo (StringExtractor& stop_packet); + + void + DidLaunchOrAttach (); + + lldb_private::Error + ConnectToDebugserver (const char *host_port); + + const char * + GetDispatchQueueNameForThread (lldb::addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name); + + static size_t + AttachInputReaderCallback (void *baton, + lldb_private::InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + +private: + //------------------------------------------------------------------ + // For ProcessGDBRemote only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote); + + lldb_private::unw_addr_space_t + GetLibUnwindAddressSpace (); + + void + DestoryLibUnwindAddressSpace (); +}; + +#endif // liblldb_ProcessGDBRemote_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp new file mode 100644 index 000000000000..051ce8f20f46 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp @@ -0,0 +1,121 @@ +//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessGDBRemoteLog.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + + +static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. +Log * +ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = g_log; + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessGDBRemoteLog::DisableLog () +{ + if (g_log) + { + delete g_log; + g_log = NULL; + } +} + +Log * +ProcessGDBRemoteLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm) +{ + DisableLog (); + g_log = new Log (log_stream_sp); + if (g_log) + { + uint32_t flag_bits = 0; + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; iPrintf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = GDBR_LOG_DEFAULT; + g_log->GetMask().SetAllFlagBits(flag_bits); + g_log->GetOptions().SetAllFlagBits(log_options); + } + return g_log; +} + +void +ProcessGDBRemoteLog::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + "\tall - turn on all available logging categories\n" + "\tbreak - log breakpoints\n" + "\tdefault - enable the default set of logging categories for liblldb\n" + "\tpackets - log gdb remote packets\n" + "\tmemory - log memory reads and writes\n" + "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n" + "\tdata-long - log memory bytes for memory reads and writes for all transactions\n" + "\tprocess - log process events and activities\n" + "\tthread - log thread events and activities\n" + "\tstep - log step related activities\n" + "\tverbose - enable verbose loggging\n" + "\twatch - log watchpoint related activities\n", ProcessGDBRemote::GetPluginNameStatic()); +} + + +void +ProcessGDBRemoteLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h new file mode 100644 index 000000000000..97580d386745 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h @@ -0,0 +1,53 @@ +//===-- ProcessGDBRemoteLog.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessGDBRemoteLog_h_ +#define liblldb_ProcessGDBRemoteLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define GDBR_LOG_VERBOSE (1u << 0) +#define GDBR_LOG_PROCESS (1u << 1) +#define GDBR_LOG_THREAD (1u << 2) +#define GDBR_LOG_PACKETS (1u << 3) +#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define GDBR_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define GDBR_LOG_BREAKPOINTS (1u << 7) +#define GDBR_LOG_WATCHPOINTS (1u << 8) +#define GDBR_LOG_STEP (1u << 9) +#define GDBR_LOG_COMM (1u << 10) +#define GDBR_LOG_ALL (UINT32_MAX) +#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS + +class ProcessGDBRemoteLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessGDBRemoteLog_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp new file mode 100644 index 000000000000..37485edf4e5d --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -0,0 +1,296 @@ +//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadGDBRemote.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/WatchpointLocation.h" + +#include "LibUnwindRegisterContext.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "StringExtractorGDBRemote.h" +#include "UnwindLibUnwind.h" +#include "UnwindMacOSXFrameBackchain.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadGDBRemote::ThreadGDBRemote (ProcessGDBRemote &process, lldb::tid_t tid) : + Thread(process, tid), + m_stop_info_stop_id (0), + m_stop_info (this), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS), + m_unwinder_ap () +{ +// ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD | GDBR_LOG_VERBOSE, "ThreadGDBRemote::ThreadGDBRemote ( pid = %i, tid = 0x%4.4x, )", m_process.GetID(), GetID()); + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, m_process.GetID(), GetID()); +} + +ThreadGDBRemote::~ThreadGDBRemote () +{ + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::~ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, m_process.GetID(), GetID()); +} + + +const char * +ThreadGDBRemote::GetInfo () +{ + return NULL; +} + + +const char * +ThreadGDBRemote::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + + +const char * +ThreadGDBRemote::GetQueueName () +{ + // Always re-fetch the dispatch queue name since it can change + if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + return GetGDBProcess().GetDispatchQueueNameForThread (m_thread_dispatch_qaddr, m_dispatch_queue_name); + return NULL; +} + +bool +ThreadGDBRemote::WillResume (StateType resume_state) +{ + // TODO: cache for next time in case we can match things up?? + ClearStackFrames(); + int signo = GetResumeSignal(); + m_stop_info.Clear(); + switch (resume_state) + { + case eStateSuspended: + case eStateStopped: + // Don't append anything for threads that should stay stopped. + break; + + case eStateRunning: + if (m_process.GetUnixSignals().SignalIsValid (signo)) + GetGDBProcess().m_continue_packet.Printf(";C%2.2x:%4.4x", signo, GetID()); + else + GetGDBProcess().m_continue_packet.Printf(";c:%4.4x", GetID()); + break; + + case eStateStepping: + if (m_process.GetUnixSignals().SignalIsValid (signo)) + GetGDBProcess().m_continue_packet.Printf(";S%2.2x:%4.4x", signo, GetID()); + else + GetGDBProcess().m_continue_packet.Printf(";s:%4.4x", GetID()); + break; + } + Thread::WillResume(resume_state); + return true; +} + +void +ThreadGDBRemote::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context + GetRegisterContext()->Invalidate(); +} + +Unwind * +ThreadGDBRemote::GetUnwinder () +{ + if (m_unwinder_ap.get() == NULL) + { + const ArchSpec target_arch (GetProcess().GetTarget().GetArchitecture ()); + if (target_arch == ArchSpec("x86_64") || target_arch == ArchSpec("i386")) + { + m_unwinder_ap.reset (new UnwindLibUnwind (*this, GetGDBProcess().GetLibUnwindAddressSpace())); + } + else + { + m_unwinder_ap.reset (new UnwindMacOSXFrameBackchain (*this)); + } + } + return m_unwinder_ap.get(); +} + +uint32_t +ThreadGDBRemote::GetStackFrameCount() +{ + Unwind *unwinder = GetUnwinder (); + if (unwinder) + return unwinder->GetFrameCount(); + return 0; +} + +// Make sure that GetStackFrameAtIndex() does NOT call GetStackFrameCount() when +// getting the stack frame at index zero! This way GetStackFrameCount() (via +// GetStackFRameData()) can call this function to get the first frame in order +// to provide the first frame to a lower call for efficiency sake (avoid +// redundant lookups in the frame symbol context). +lldb::StackFrameSP +ThreadGDBRemote::GetStackFrameAtIndex (uint32_t idx) +{ + + StackFrameSP frame_sp (m_frames.GetFrameAtIndex(idx)); + + if (frame_sp.get()) + return frame_sp; + + // Don't try and fetch a frame while process is running +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (m_process.IsRunning()) +// return frame_sp; + + // Special case the first frame (idx == 0) so that we don't need to + // know how many stack frames there are to get it. If we need any other + // frames, then we do need to know if "idx" is a valid index. + if (idx == 0) + { + // If this is the first frame, we want to share the thread register + // context with the stack frame at index zero. + GetRegisterContext(); + assert (m_reg_context_sp.get()); + frame_sp.reset (new StackFrame (idx, *this, m_reg_context_sp, m_reg_context_sp->GetSP(), m_reg_context_sp->GetPC())); + } + else if (idx < GetStackFrameCount()) + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + { + addr_t pc, cfa; + if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) + frame_sp.reset (new StackFrame (idx, *this, cfa, pc)); + } + } + m_frames.SetFrameAtIndex(idx, frame_sp); + return frame_sp; +} + +void +ThreadGDBRemote::ClearStackFrames () +{ + Unwind *unwinder = GetUnwinder (); + if (unwinder) + unwinder->Clear(); + Thread::ClearStackFrames(); +} + + +bool +ThreadGDBRemote::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadGDBRemote::Dump(Log *log, uint32_t index) +{ +} + + +bool +ThreadGDBRemote::ShouldStop (bool &step_more) +{ + return true; +} +RegisterContext * +ThreadGDBRemote::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp.reset (CreateRegisterContextForFrame (NULL)); + return m_reg_context_sp.get(); +} + +RegisterContext * +ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame) +{ + const bool read_all_registers_at_once = false; + uint32_t frame_idx = 0; + + if (frame) + frame_idx = frame->GetID(); + + if (frame_idx == 0) + return new GDBRemoteRegisterContext (*this, frame, GetGDBProcess().m_register_info, read_all_registers_at_once); + else if (m_unwinder_ap.get() && frame_idx < m_unwinder_ap->GetFrameCount()) + return m_unwinder_ap->CreateRegisterContextForFrame (frame); + return NULL; +} + +bool +ThreadGDBRemote::SaveFrameZeroState (RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + checkpoint.SetStackID(frame_sp->GetStackID()); + return frame_sp->GetRegisterContext()->ReadAllRegisterValues (checkpoint.GetData()); + } + return false; +} + +bool +ThreadGDBRemote::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + bool ret = frame_sp->GetRegisterContext()->WriteAllRegisterValues (checkpoint.GetData()); + frame_sp->GetRegisterContext()->Invalidate(); + ClearStackFrames(); + return ret; + } + return false; +} + +bool +ThreadGDBRemote::GetRawStopReason (StopInfo *stop_info) +{ + if (m_stop_info_stop_id != m_process.GetStopID()) + { + char packet[256]; + const int packet_len = snprintf(packet, sizeof(packet), "qThreadStopInfo%x", GetID()); + assert (packet_len < (sizeof(packet) - 1)); + StringExtractorGDBRemote stop_packet; + if (GetGDBProcess().GetGDBRemote().SendPacketAndWaitForResponse(packet, stop_packet, 1, false)) + { + std::string copy(stop_packet.GetStringRef()); + GetGDBProcess().SetThreadStopInfo (stop_packet); + // The process should have set the stop info stop ID and also + // filled this thread in with valid stop info + if (m_stop_info_stop_id != m_process.GetStopID()) + { + //ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "warning: qThreadStopInfo problem: '%s' => '%s'", packet, stop_packet.GetStringRef().c_str()); + printf("warning: qThreadStopInfo problem: '%s' => '%s'\n\torig '%s'\n", packet, stop_packet.GetStringRef().c_str(), copy.c_str()); /// REMOVE THIS + return false; + } + } + } + *stop_info = m_stop_info; + return true; +} + + diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h new file mode 100644 index 000000000000..3fa4ae09a2a2 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -0,0 +1,156 @@ +//===-- ThreadGDBRemote.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadGDBRemote_h_ +#define liblldb_ThreadGDBRemote_h_ + +#include + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "MachException.h" +#include "libunwind.h" + +class StringExtractor; +class ProcessGDBRemote; + +class ThreadGDBRemote : public lldb_private::Thread +{ +public: + ThreadGDBRemote (ProcessGDBRemote &process, lldb::tid_t tid); + + virtual + ~ThreadGDBRemote (); + + virtual bool + WillResume (lldb::StateType resume_state); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetInfo (); + + virtual const char * + GetName (); + + virtual const char * + GetQueueName (); + + virtual lldb_private::RegisterContext * + GetRegisterContext (); + + virtual lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint); + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint); + + virtual uint32_t + GetStackFrameCount(); + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx); + + virtual void + ClearStackFrames (); + + ProcessGDBRemote & + GetGDBProcess () + { + return (ProcessGDBRemote &)m_process; + } + + const ProcessGDBRemote & + GetGDBProcess () const + { + return (ProcessGDBRemote &)m_process; + } + + void + Dump (lldb_private::Log *log, uint32_t index); + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + lldb_private::Thread::StopInfo & + GetStopInfoRef () + { + return m_stop_info; + } + + uint32_t + GetStopInfoStopID() + { + return m_stop_info_stop_id; + } + + void + SetStopInfoStopID (uint32_t stop_id) + { + m_stop_info_stop_id = stop_id; + } + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + uint32_t m_stop_info_stop_id; + lldb_private::Thread::StopInfo m_stop_info; + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + std::auto_ptr m_unwinder_ap; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + + lldb_private::Unwind * + GetUnwinder (); + + void + SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id); + + virtual bool + GetRawStopReason (StopInfo *stop_info); + + +}; + +#endif // liblldb_ThreadGDBRemote_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 000000000000..989e49452fb7 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,211 @@ +//===-- DWARFAbbreviationDeclaration.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFAbbreviationDeclaration.h" + +#include "lldb/Core/dwarf.h" + +#include "DWARFFormValue.h" + +using namespace lldb_private; + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() : + m_code (InvalidCode), + m_tag (0), + m_has_children (0), + m_attributes() +{ +} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children) : + m_code (InvalidCode), + m_tag (tag), + m_has_children (has_children), + m_attributes() +{ +} + +bool +DWARFAbbreviationDeclaration::Extract(const DataExtractor& data, uint32_t* offset_ptr) +{ + return Extract(data, offset_ptr, data.GetULEB128(offset_ptr)); +} + +bool +DWARFAbbreviationDeclaration::Extract(const DataExtractor& data, uint32_t* offset_ptr, dw_uleb128_t code) +{ + m_code = code; + m_attributes.clear(); + if (m_code) + { + m_tag = data.GetULEB128(offset_ptr); + m_has_children = data.GetU8(offset_ptr); + + while (data.ValidOffset(*offset_ptr)) + { + dw_attr_t attr = data.GetULEB128(offset_ptr); + dw_form_t form = data.GetULEB128(offset_ptr); + + if (attr && form) + m_attributes.push_back(DWARFAttribute(attr, form)); + else + break; + } + + return m_tag != 0; + } + else + { + m_tag = 0; + m_has_children = 0; + } + + return false; +} + + +void +DWARFAbbreviationDeclaration::Dump(Stream *s) const +{ +// *ostrm_ptr << std::setfill(' ') << std::dec << '[' << std::setw(3) << std::right << m_code << ']' << ' ' << std::setw(30) << std::left << DW_TAG_value_to_name(m_tag) << DW_CHILDREN_value_to_name(m_has_children) << std::endl; +// +// DWARFAttribute::const_iterator pos; +// +// for (pos = m_attributes.begin(); pos != m_attributes.end(); ++pos) +// *ostrm_ptr << " " << std::setw(29) << std::left << DW_AT_value_to_name(pos->attr()) << ' ' << DW_FORM_value_to_name(pos->form()) << std::endl; +// +// *ostrm_ptr << std::endl; +} + + + +bool +DWARFAbbreviationDeclaration::IsValid() +{ + return m_code != 0 && m_tag != 0; +} + + +void +DWARFAbbreviationDeclaration::CopyExcludingAddressAttributes(const DWARFAbbreviationDeclaration& abbr_decl, const uint32_t idx) +{ + m_code = abbr_decl.Code(); // Invalidate the code since that can't be copied safely. + m_tag = abbr_decl.Tag(); + m_has_children = abbr_decl.HasChildren(); + + const DWARFAttribute::collection& attributes = abbr_decl.Attributes(); + const uint32_t num_abbr_decl_attributes = attributes.size(); + + dw_attr_t attr; + dw_form_t form; + uint32_t i; + + for (i = 0; i < num_abbr_decl_attributes; ++i) + { + attributes[i].get(attr, form); + switch (attr) + { + case DW_AT_location: + case DW_AT_frame_base: + // Only add these if they are location expressions (have a single + // value) and not location lists (have a lists of location + // expressions which are only valid over specific address ranges) + if (DWARFFormValue::IsBlockForm(form)) + m_attributes.push_back(DWARFAttribute(attr, form)); + break; + + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + case DW_AT_entry_pc: + // Don't add these attributes + if (i >= idx) + break; + // Fall through and add attribute + default: + // Add anything that isn't address related + m_attributes.push_back(DWARFAttribute(attr, form)); + break; + } + } +} + +void +DWARFAbbreviationDeclaration::CopyChangingStringToStrp( + const DWARFAbbreviationDeclaration& abbr_decl, + const DataExtractor& debug_info_data, + dw_offset_t debug_info_offset, + const DWARFCompileUnit* cu, + const uint32_t strp_min_len +) +{ + m_code = InvalidCode; + m_tag = abbr_decl.Tag(); + m_has_children = abbr_decl.HasChildren(); + + const DWARFAttribute::collection& attributes = abbr_decl.Attributes(); + const uint32_t num_abbr_decl_attributes = attributes.size(); + + dw_attr_t attr; + dw_form_t form; + uint32_t i; + dw_offset_t offset = debug_info_offset; + + for (i = 0; i < num_abbr_decl_attributes; ++i) + { + attributes[i].get(attr, form); + dw_offset_t attr_offset = offset; + DWARFFormValue::SkipValue(form, debug_info_data, &offset, cu); + + if (form == DW_FORM_string && ((offset - attr_offset) >= strp_min_len)) + m_attributes.push_back(DWARFAttribute(attr, DW_FORM_strp)); + else + m_attributes.push_back(DWARFAttribute(attr, form)); + } +} + + +uint32_t +DWARFAbbreviationDeclaration::FindAttributeIndex(dw_attr_t attr) const +{ + uint32_t i; + const uint32_t kNumAttributes = m_attributes.size(); + for (i = 0; i < kNumAttributes; ++i) + { + if (m_attributes[i].get_attr() == attr) + return i; + } + return DW_INVALID_INDEX; +} + + +bool +DWARFAbbreviationDeclaration::operator == (const DWARFAbbreviationDeclaration& rhs) const +{ + return Tag() == rhs.Tag() + && HasChildren() == rhs.HasChildren() + && Attributes() == rhs.Attributes(); +} + +#if 0 +DWARFAbbreviationDeclaration::Append(BinaryStreamBuf& out_buff) const +{ + out_buff.Append32_as_ULEB128(Code()); + out_buff.Append32_as_ULEB128(Tag()); + out_buff.Append8(HasChildren()); + const uint32_t kNumAttributes = m_attributes.size(); + for (uint32_t i = 0; i < kNumAttributes; ++i) + { + out_buff.Append32_as_ULEB128(m_attributes[i].attr()); + out_buff.Append32_as_ULEB128(m_attributes[i].form()); + } + out_buff.Append8(0); // Output a zero for attr (faster than using Append32_as_ULEB128) + out_buff.Append8(0); // Output a zero for attr (faster than using Append32_as_ULEB128) +} +#endif // 0 diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h new file mode 100644 index 000000000000..7959f264dfe4 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h @@ -0,0 +1,77 @@ +//===-- DWARFAbbreviationDeclaration.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFAbbreviationDeclaration_h_ +#define liblldb_DWARFAbbreviationDeclaration_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFAttribute.h" + +class DWARFCompileUnit; + +class DWARFAbbreviationDeclaration +{ +public: + enum { InvalidCode = 0 }; + DWARFAbbreviationDeclaration(); + + // For hand crafting an abbreviation declaration + DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children); + void AddAttribute(const DWARFAttribute& attr) + { + m_attributes.push_back(attr); + } + + dw_uleb128_t Code() const { return m_code; } + void SetCode(dw_uleb128_t code) { m_code = code; } + dw_tag_t Tag() const { return m_tag; } + bool HasChildren() const { return m_has_children; } + uint32_t NumAttributes() const { return m_attributes.size(); } + dw_attr_t GetAttrByIndex(uint32_t idx) const { return m_attributes.size() > idx ? m_attributes[idx].get_attr() : 0; } + dw_form_t GetFormByIndex(uint32_t idx) const { return m_attributes.size() > idx ? m_attributes[idx].get_form() : 0; } + bool GetAttrAndFormByIndex(uint32_t idx, dw_attr_t& attr, dw_form_t& form) const + { + if (m_attributes.size() > idx) + { + m_attributes[idx].get(attr, form); + return true; + } + attr = form = 0; + return false; + } + + // idx is assumed to be valid when calling GetAttrAndFormByIndexUnchecked() + void GetAttrAndFormByIndexUnchecked(uint32_t idx, dw_attr_t& attr, dw_form_t& form) const + { + m_attributes[idx].get(attr, form); + } + void CopyExcludingAddressAttributes(const DWARFAbbreviationDeclaration& abbr_decl, const uint32_t idx); + void CopyChangingStringToStrp( + const DWARFAbbreviationDeclaration& abbr_decl, + const lldb_private::DataExtractor& debug_info_data, + dw_offset_t debug_info_offset, + const DWARFCompileUnit* cu, + const uint32_t strp_min_len); + uint32_t FindAttributeIndex(dw_attr_t attr) const; + bool Extract(const lldb_private::DataExtractor& data, uint32_t* offset_ptr); + bool Extract(const lldb_private::DataExtractor& data, uint32_t* offset_ptr, dw_uleb128_t code); +// void Append(BinaryStreamBuf& out_buff) const; + bool IsValid(); + void Dump(lldb_private::Stream *s) const; + bool operator == (const DWARFAbbreviationDeclaration& rhs) const; +// DWARFAttribute::collection& Attributes() { return m_attributes; } + const DWARFAttribute::collection& Attributes() const { return m_attributes; } +protected: + dw_uleb128_t m_code; + dw_tag_t m_tag; + uint8_t m_has_children; + DWARFAttribute::collection m_attributes; +}; + +#endif // liblldb_DWARFAbbreviationDeclaration_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h new file mode 100644 index 000000000000..0b44926cba02 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h @@ -0,0 +1,45 @@ +//===-- DWARFAttribute.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFAttribute_h_ +#define liblldb_DWARFAttribute_h_ + +#include "DWARFDefines.h" +#include + +class DWARFAttribute +{ +public: + DWARFAttribute(dw_attr_t attr, dw_form_t form) : + m_attr_form ( attr << 16 | form ) + { + } + + void set(dw_attr_t attr, dw_form_t form) { m_attr_form = (attr << 16) | form; } + void set_attr(dw_attr_t attr) { m_attr_form = (m_attr_form & 0x0000ffffu) | (attr << 16); } + void set_form(dw_form_t form) { m_attr_form = (m_attr_form & 0xffff0000u) | form; } + dw_attr_t get_attr() const { return m_attr_form >> 16; } + dw_form_t get_form() const { return m_attr_form; } + void get(dw_attr_t& attr, dw_form_t& form) const + { + register uint32_t attr_form = m_attr_form; + attr = attr_form >> 16; + form = attr_form; + } + bool operator == (const DWARFAttribute& rhs) const { return m_attr_form == rhs.m_attr_form; } + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + +protected: + uint32_t m_attr_form; // Upper 16 bits is attribute, lower 16 bits is form +}; + + +#endif // liblldb_DWARFAttribute_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp new file mode 100644 index 000000000000..e7f92c6eff25 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,770 @@ +//===-- DWARFCompileUnit.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFCompileUnit.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + +using namespace lldb_private; +using namespace std; + +extern int g_verbose; + +DWARFCompileUnit::DWARFCompileUnit(SymbolFileDWARF* m_dwarf2Data) : + m_dwarf2Data ( m_dwarf2Data ), + m_offset ( DW_INVALID_OFFSET ), + m_length ( 0 ), + m_version ( 0 ), + m_abbrevs ( NULL ), + m_addr_size ( DWARFCompileUnit::GetDefaultAddressSize() ), + m_base_addr ( 0 ), + m_die_array (), + m_aranges_ap (), + m_user_data ( NULL ) +{ +} + +void +DWARFCompileUnit::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_length = 0; + m_version = 0; + m_abbrevs = NULL; + m_addr_size = DWARFCompileUnit::GetDefaultAddressSize(); + m_base_addr = 0; + m_die_array.clear(); + m_aranges_ap.reset(); + m_user_data = NULL; +} + +bool +DWARFCompileUnit::Extract(const DataExtractor &debug_info, uint32_t* offset_ptr) +{ + Clear(); + + m_offset = *offset_ptr; + + if (debug_info.ValidOffset(*offset_ptr)) + { + dw_offset_t abbr_offset; + const DWARFDebugAbbrev *abbr = m_dwarf2Data->DebugAbbrev(); + m_length = debug_info.GetU32(offset_ptr); + m_version = debug_info.GetU16(offset_ptr); + abbr_offset = debug_info.GetU32(offset_ptr); + m_addr_size = debug_info.GetU8 (offset_ptr); + + bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool abbr_offset_OK = m_dwarf2Data->get_debug_abbrev_data().ValidOffset(abbr_offset); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + + if (length_OK && version_OK && addr_size_OK && abbr_offset_OK && abbr != NULL) + { + m_abbrevs = abbr->GetAbbreviationDeclarationSet(abbr_offset); + return true; + } + + // reset the offset to where we tried to parse from if anything went wrong + *offset_ptr = m_offset; + } + + return false; +} + + +dw_offset_t +DWARFCompileUnit::Extract(dw_offset_t offset, const DataExtractor& debug_info_data, const DWARFAbbreviationDeclarationSet* abbrevs) +{ + Clear(); + + m_offset = offset; + + if (debug_info_data.ValidOffset(offset)) + { + m_length = debug_info_data.GetU32(&offset); + m_version = debug_info_data.GetU16(&offset); + bool abbrevs_OK = debug_info_data.GetU32(&offset) == abbrevs->GetOffset(); + m_abbrevs = abbrevs; + m_addr_size = debug_info_data.GetU8 (&offset); + + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + + if (version_OK && addr_size_OK && abbrevs_OK && debug_info_data.ValidOffset(offset)) + return offset; + } + return DW_INVALID_OFFSET; +} + +void +DWARFCompileUnit::ClearDIEs(bool keep_compile_unit_die) +{ + if (m_die_array.size() > 1) + { + // std::vectors never get any smaller when resized to a smaller size, + // or when clear() or erase() are called, the size will report that it + // is smaller, but the memory allocated remains intact (call capacity() + // to see this). So we need to create a temporary vector and swap the + // contents which will cause just the internal pointers to be swapped + // so that when "tmp_array" goes out of scope, it will destroy the + // contents. + + // Save at least the compile unit DIE + DWARFDebugInfoEntry::collection tmp_array; + m_die_array.swap(tmp_array); + if (keep_compile_unit_die) + m_die_array.push_back(tmp_array.front()); + } +} + +//---------------------------------------------------------------------- +// ParseCompileUnitDIEsIfNeeded +// +// Parses a compile unit and indexes its DIEs if it already hasn't been +// done. +//---------------------------------------------------------------------- +size_t +DWARFCompileUnit::ExtractDIEsIfNeeded (bool cu_die_only) +{ + const size_t initial_die_array_size = m_die_array.size(); + if ((cu_die_only && initial_die_array_size > 0) || initial_die_array_size > 1) + return 0; // Already parsed + + Timer scoped_timer (__PRETTY_FUNCTION__, + "%8.8x: DWARFCompileUnit::ExtractDIEsIfNeeded( cu_die_only = %i )", + m_offset, + cu_die_only); + + // Set the offset to that of the first DIE + uint32_t offset = GetFirstDIEOffset(); + const dw_offset_t next_cu_offset = GetNextCompileUnitOffset(); + DWARFDebugInfoEntry die; + // Keep a flat array of the DIE for binary lookup by DIE offset + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); +// if (log) +// log->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, abbr_offset = 0x%8.8x, addr_size = 0x%2.2x", +// cu->GetOffset(), +// cu->GetLength(), +// cu->GetVersion(), +// cu->GetAbbrevOffset(), +// cu->GetAddressByteSize()); + + uint32_t depth = 0; + // We are in our compile unit, parse starting at the offset + // we were told to parse + while (die.Extract(m_dwarf2Data, this, &offset)) + { + if (log) + log->Printf("0x%8.8x: %*.*s%s%s", + die.GetOffset(), + depth * 2, depth * 2, "", + DW_TAG_value_to_name (die.Tag()), + die.HasChildren() ? " *" : ""); + if (cu_die_only) + { + AddDIE(die); + return 1; + } + else if (depth == 0 && initial_die_array_size == 1) + { + // Don't append the CU die as we already did that + } + else + { + AddDIE(die); + } + + const DWARFAbbreviationDeclaration* abbrDecl = die.GetAbbreviationDeclarationPtr(); + if (abbrDecl) + { + // Normal DIE + if (abbrDecl->HasChildren()) + ++depth; + } + else + { + // NULL DIE. + if (depth > 0) + --depth; + else + break; // We are done with this compile unit! + } + + assert(offset <= next_cu_offset); + } + SetDIERelations(); + return m_die_array.size(); +} + + +dw_offset_t +DWARFCompileUnit::GetAbbrevOffset() const +{ + return m_abbrevs ? m_abbrevs->GetOffset() : DW_INVALID_OFFSET; +} + + + +bool +DWARFCompileUnit::Verify(Stream *s) const +{ + const DataExtractor& debug_info = m_dwarf2Data->get_debug_info_data(); + bool valid_offset = debug_info.ValidOffset(m_offset); + bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool abbr_offset_OK = m_dwarf2Data->get_debug_abbrev_data().ValidOffset(GetAbbrevOffset()); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + bool verbose = s->GetVerbose(); + if (valid_offset && length_OK && version_OK && addr_size_OK && abbr_offset_OK) + { + if (verbose) + s->Printf(" 0x%8.8x: OK\n", m_offset); + return true; + } + else + { + s->Printf(" 0x%8.8x: ", m_offset); + + m_dwarf2Data->get_debug_info_data().Dump (s, m_offset, lldb::eFormatHex, 1, Size(), 32, LLDB_INVALID_ADDRESS, 0, 0); + s->EOL(); + if (valid_offset) + { + if (!length_OK) + s->Printf(" The length (0x%8.8x) for this compile unit is too large for the .debug_info provided.\n", m_length); + if (!version_OK) + s->Printf(" The 16 bit compile unit header version is not supported.\n"); + if (!abbr_offset_OK) + s->Printf(" The offset into the .debug_abbrev section (0x%8.8x) is not valid.\n", GetAbbrevOffset()); + if (!addr_size_OK) + s->Printf(" The address size is unsupported: 0x%2.2x\n", m_addr_size); + } + else + s->Printf(" The start offset of the compile unit header in the .debug_info is invalid.\n"); + } + return false; +} + + +void +DWARFCompileUnit::Dump(Stream *s) const +{ + s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at {0x%8.8x})\n", + m_offset, m_length, m_version, GetAbbrevOffset(), m_addr_size, GetNextCompileUnitOffset()); +} + + +static uint8_t g_default_addr_size = 4; + +uint8_t +DWARFCompileUnit::GetAddressByteSize(const DWARFCompileUnit* cu) +{ + if (cu) + return cu->GetAddressByteSize(); + return DWARFCompileUnit::GetDefaultAddressSize(); +} + +uint8_t +DWARFCompileUnit::GetDefaultAddressSize() +{ + return g_default_addr_size; +} + +void +DWARFCompileUnit::SetDefaultAddressSize(uint8_t addr_size) +{ + g_default_addr_size = addr_size; +} + +bool +DWARFCompileUnit::LookupAddress +( + const dw_addr_t address, + DWARFDebugInfoEntry** function_die_handle, + DWARFDebugInfoEntry** block_die_handle +) +{ + bool success = false; + + if (function_die_handle != NULL && DIE()) + { + if (m_aranges_ap.get() == NULL) + { + m_aranges_ap.reset(new DWARFDebugAranges()); + m_die_array.front().BuildFunctionAddressRangeTable(m_dwarf2Data, this, m_aranges_ap.get()); + } + + // Re-check the aranges auto pointer contents in case it was created above + if (m_aranges_ap.get() != NULL) + { + *function_die_handle = GetDIEPtr(m_aranges_ap->FindAddress(address)); + if (*function_die_handle != NULL) + { + success = true; + if (block_die_handle != NULL) + { + DWARFDebugInfoEntry* child = (*function_die_handle)->GetFirstChild(); + while (child) + { + if (child->LookupAddress(address, m_dwarf2Data, this, NULL, block_die_handle)) + break; + child = child->GetSibling(); + } + } + } + } + } + return success; +} + +//---------------------------------------------------------------------- +// SetDIERelations() +// +// We read in all of the DIE entries into our flat list of DIE entries +// and now we need to go back through all of them and set the parent, +// sibling and child pointers for quick DIE navigation. +//---------------------------------------------------------------------- +void +DWARFCompileUnit::SetDIERelations() +{ +#if 0 + // Compute average bytes per DIE + // + // We can figure out what the average number of bytes per DIE is + // to help us pre-allocate the correct number of m_die_array + // entries so we don't end up doing a lot of memory copies as we + // are creating our DIE array when parsing + // + // Enable this code by changing "#if 0" above to "#if 1" and running + // the dsymutil or dwarfdump with a bunch of dwarf files and see what + // the running average ends up being in the stdout log. + static size_t g_total_cu_debug_info_size = 0; + static size_t g_total_num_dies = 0; + static size_t g_min_bytes_per_die = UINT_MAX; + static size_t g_max_bytes_per_die = 0; + const size_t num_dies = m_die_array.size(); + const size_t cu_debug_info_size = GetDebugInfoSize(); + const size_t bytes_per_die = cu_debug_info_size / num_dies; + if (g_min_bytes_per_die > bytes_per_die) + g_min_bytes_per_die = bytes_per_die; + if (g_max_bytes_per_die < bytes_per_die) + g_max_bytes_per_die = bytes_per_die; + if (g_total_cu_debug_info_size == 0) + { + cout << " min max avg" << endl + << "n dies cu size bpd bpd bpd bpd" << endl + << "------ -------- --- === === ===" << endl; + } + g_total_cu_debug_info_size += cu_debug_info_size; + g_total_num_dies += num_dies; + const size_t avg_bytes_per_die = g_total_cu_debug_info_size / g_total_num_dies; + cout + << DECIMAL_WIDTH(6) << num_dies << ' ' + << DECIMAL_WIDTH(8) << cu_debug_info_size << ' ' + << DECIMAL_WIDTH(3) << bytes_per_die << ' ' + << DECIMAL_WIDTH(3) << g_min_bytes_per_die << ' ' + << DECIMAL_WIDTH(3) << g_max_bytes_per_die << ' ' + << DECIMAL_WIDTH(3) << avg_bytes_per_die + << endl; +#endif + if (m_die_array.empty()) + return; + DWARFDebugInfoEntry* die_array_begin = &m_die_array.front(); + DWARFDebugInfoEntry* die_array_end = &m_die_array.back(); + DWARFDebugInfoEntry* curr_die; + // We purposely are skipping the last element in the array in the loop below + // so that we can always have a valid next item + for (curr_die = die_array_begin; curr_die < die_array_end; ++curr_die) + { + // Since our loop doesn't include the last element, we can always + // safely access the next die in the array. + DWARFDebugInfoEntry* next_die = curr_die + 1; + + const DWARFAbbreviationDeclaration* curr_die_abbrev = curr_die->GetAbbreviationDeclarationPtr(); + + if (curr_die_abbrev) + { + // Normal DIE + if (curr_die_abbrev->HasChildren()) + next_die->SetParent(curr_die); + else + curr_die->SetSibling(next_die); + } + else + { + // NULL DIE that terminates a sibling chain + DWARFDebugInfoEntry* parent = curr_die->GetParent(); + if (parent) + parent->SetSibling(next_die); + } + } + + // Since we skipped the last element, we need to fix it up! + if (die_array_begin < die_array_end) + curr_die->SetParent(die_array_begin); + +#if 0 + // The code below will dump the DIE relations in case any modification + // is done to the above code. This dump can be used in a diff to make + // sure that no functionality is lost. + { + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + puts("offset parent sibling child"); + puts("-------- -------- -------- --------"); + for (pos = m_die_array.begin(); pos != end; ++pos) + { + const DWARFDebugInfoEntry& die_ref = *pos; + const DWARFDebugInfoEntry* p = die_ref.GetParent(); + const DWARFDebugInfoEntry* s = die_ref.GetSibling(); + const DWARFDebugInfoEntry* c = die_ref.GetFirstChild(); + printf("%.8x: %.8x %.8x %.8x\n", die_ref.GetOffset(), + p ? p->GetOffset() : 0, + s ? s->GetOffset() : 0, + c ? c->GetOffset() : 0); + } + } +#endif + +} +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) +{ + return die1.GetOffset() < die2.GetOffset(); +} + +//---------------------------------------------------------------------- +// GetDIEPtr() +// +// Get the DIE (Debug Information Entry) with the specified offset. +//---------------------------------------------------------------------- +DWARFDebugInfoEntry* +DWARFCompileUnit::GetDIEPtr(dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + ExtractDIEsIfNeeded (false); + DWARFDebugInfoEntry compare_die; + compare_die.SetOffset(die_offset); + DWARFDebugInfoEntry::iterator end = m_die_array.end(); + DWARFDebugInfoEntry::iterator pos = lower_bound(m_die_array.begin(), end, compare_die, CompareDIEOffset); + if (pos != end) + { + if (die_offset == (*pos).GetOffset()) + return &(*pos); + } + } + return NULL; // Not found in any compile units +} + +//---------------------------------------------------------------------- +// GetDIEPtrContainingOffset() +// +// Get the DIE (Debug Information Entry) that contains the specified +// .debug_info offset. +//---------------------------------------------------------------------- +const DWARFDebugInfoEntry* +DWARFCompileUnit::GetDIEPtrContainingOffset(dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + ExtractDIEsIfNeeded (false); + DWARFDebugInfoEntry compare_die; + compare_die.SetOffset(die_offset); + DWARFDebugInfoEntry::iterator end = m_die_array.end(); + DWARFDebugInfoEntry::iterator pos = lower_bound(m_die_array.begin(), end, compare_die, CompareDIEOffset); + if (pos != end) + { + if (die_offset >= (*pos).GetOffset()) + { + DWARFDebugInfoEntry::iterator next = pos + 1; + if (next != end) + { + if (die_offset < (*next).GetOffset()) + return &(*pos); + } + } + } + } + return NULL; // Not found in any compile units +} + + + +size_t +DWARFCompileUnit::AppendDIEsWithTag (const dw_tag_t tag, DWARFDIECollection& dies, uint32_t depth) const +{ + size_t old_size = dies.Size(); + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = m_die_array.begin(); pos != end; ++pos) + { + if (pos->Tag() == tag) + dies.Insert(&(*pos)); + } + + // Return the number of DIEs added to the collection + return dies.Size() - old_size; +} + +void +DWARFCompileUnit::AddGlobalDIEByIndex (uint32_t die_idx) +{ + m_global_die_indexes.push_back (die_idx); +} + + +void +DWARFCompileUnit::AddGlobal (const DWARFDebugInfoEntry* die) +{ + // Indexes to all file level global and static variables + m_global_die_indexes; + + if (m_die_array.empty()) + return; + + const DWARFDebugInfoEntry* first_die = &m_die_array[0]; + const DWARFDebugInfoEntry* end = first_die + m_die_array.size(); + if (first_die <= die && die < end) + m_global_die_indexes.push_back (die - first_die); +} + + +void +DWARFCompileUnit::Index +( + lldb_private::UniqueCStringMap& name_to_function_die, + lldb_private::UniqueCStringMap& name_to_inlined_die, + lldb_private::UniqueCStringMap& name_to_global_die, + lldb_private::UniqueCStringMap& name_to_type_die +) +{ + + const DataExtractor* debug_str = &m_dwarf2Data->get_debug_str_data(); + + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator begin = m_die_array.begin(); + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = begin; pos != end; ++pos) + { + const DWARFDebugInfoEntry &die = *pos; + + const dw_tag_t tag = die.Tag(); + + switch (tag) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_subroutine_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_typedef: + case DW_TAG_namespace: + case DW_TAG_variable: + break; + + default: + continue; + } + + DWARFDebugInfoEntry::Attributes attributes; + const char *name = NULL; + const char *mangled = NULL; + bool is_variable = false; + bool is_declaration = false; + bool is_artificial = false; + bool has_address = false; + bool has_location = false; + bool is_global_or_static_variable = false; + const size_t num_attributes = die.GetAttributes(m_dwarf2Data, this, attributes); + if (num_attributes > 0) + { + uint32_t i; + + dw_tag_t tag = die.Tag(); + + is_variable = tag == DW_TAG_variable; + + for (i=0; iTag()) + { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We could theoretically + // add these if we wanted to by introspecting into the DW_AT_location and seeing + // if the location describes a hard coded address, but we dont want the performance + // penalty of that right now. + is_global_or_static_variable = false; +// if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) +// { +// // If we have valid block data, then we have location expression bytes +// // that are fixed (not a location list). +// const uint8_t *block_data = form_value.BlockData(); +// if (block_data) +// { +// uint32_t block_length = form_value.Unsigned(); +// if (block_length == 1 + attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) +// { +// if (block_data[0] == DW_OP_addr) +// add_die = true; +// } +// } +// } + parent_die = NULL; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + is_global_or_static_variable = true; + parent_die = NULL; // Terminate the while loop. + break; + + default: + parent_die = parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + } + } + } + + switch (tag) + { + case DW_TAG_subprogram: + if (has_address) + { + if (name && name[0]) + { + if ((name[0] == '-' || name[0] == '+') && name[1] == '[') + { + int name_len = strlen (name); + // Objective C methods must have at least: + // "-[" or "+[" prefix + // One character for a class name + // One character for the space between the class name + // One character for the method name + // "]" suffix + if (name_len >= 6 && name[name_len - 1] == ']') + { + const char *method_name = strchr (name, ' '); + if (method_name) + { + // Skip the space + ++method_name; + // Extract the objective C basename and add it to the + // accelerator tables + size_t method_name_len = name_len - (method_name - name) - 1; + ConstString method_const_str (method_name, method_name_len); + name_to_function_die.Append(method_const_str.AsCString(), die.GetOffset()); + } + } + } + name_to_function_die.Append(ConstString(name).AsCString(), die.GetOffset()); + } + if (mangled && mangled[0]) + name_to_function_die.Append(ConstString(mangled).AsCString(), die.GetOffset()); + } + break; + + case DW_TAG_inlined_subroutine: + if (has_address) + { + if (name && name[0]) + name_to_inlined_die.Append(ConstString(name).AsCString(), die.GetOffset()); + if (mangled && mangled[0]) + name_to_inlined_die.Append(ConstString(mangled).AsCString(), die.GetOffset()); + } + break; + + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_subroutine_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_typedef: + case DW_TAG_namespace: + if (name && is_declaration == false) + { + name_to_type_die.Append(ConstString(name).AsCString(), die.GetOffset()); + } + break; + + case DW_TAG_variable: + if (name && has_location && is_global_or_static_variable) + { + AddGlobalDIEByIndex (std::distance (begin, pos)); + name_to_global_die.Append(ConstString(name).AsCString(), die.GetOffset()); + } + break; + + default: + continue; + } + } +} + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h new file mode 100644 index 000000000000..44bbbfe5ba40 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -0,0 +1,168 @@ +//===-- DWARFCompileUnit.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFCompileUnit_h_ +#define SymbolFileDWARF_DWARFCompileUnit_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFDebugInfoEntry.h" + +class DWARFCompileUnit +{ +public: + DWARFCompileUnit(SymbolFileDWARF* dwarf2Data); + + bool Extract(const lldb_private::DataExtractor &debug_info, uint32_t* offset_ptr); + dw_offset_t Extract(dw_offset_t offset, const lldb_private::DataExtractor& debug_info_data, const DWARFAbbreviationDeclarationSet* abbrevs); + size_t ExtractDIEsIfNeeded (bool cu_die_only); + bool LookupAddress( + const dw_addr_t address, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + size_t AppendDIEsWithTag (const dw_tag_t tag, DWARFDIECollection& matching_dies, uint32_t depth = UINT_MAX) const; + void Clear(); + bool Verify(lldb_private::Stream *s) const; + void Dump(lldb_private::Stream *s) const; + dw_offset_t GetOffset() const { return m_offset; } + uint32_t Size() const { return 11; /* Size in bytes of the compile unit header */ } + bool ContainsDIEOffset(dw_offset_t die_offset) const { return die_offset >= GetFirstDIEOffset() && die_offset < GetNextCompileUnitOffset(); } + dw_offset_t GetFirstDIEOffset() const { return m_offset + Size(); } + dw_offset_t GetNextCompileUnitOffset() const { return m_offset + m_length + 4; } + size_t GetDebugInfoSize() const { return m_length + 4 - Size(); /* Size in bytes of the .debug_info data associated with this compile unit. */ } + uint32_t GetLength() const { return m_length; } + uint16_t GetVersion() const { return m_version; } + const DWARFAbbreviationDeclarationSet* GetAbbreviations() const { return m_abbrevs; } + dw_offset_t GetAbbrevOffset() const; + uint8_t GetAddressByteSize() const { return m_addr_size; } + dw_addr_t GetBaseAddress() const { return m_base_addr; } + void ClearDIEs(bool keep_compile_unit_die); + + void + SetBaseAddress(dw_addr_t base_addr) + { + m_base_addr = base_addr; + } + + void + SetDIERelations(); + + const DWARFDebugInfoEntry* + GetCompileUnitDIEOnly() + { + ExtractDIEsIfNeeded (true); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + const DWARFDebugInfoEntry* + DIE() + { + ExtractDIEsIfNeeded (false); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + void + AddDIE(DWARFDebugInfoEntry& die) + { + // The average bytes per DIE entry has been seen to be + // around 14-20 so lets pre-reserve the needed memory for + // our DIE entries accordingly. Search forward for "Compute + // average bytes per DIE" to see #if'ed out code that does + // that determination. + + // Only reserve the memory if we are adding children of + // the main compile unit DIE. The compile unit DIE is always + // the first entry, so if our size is 1, then we are adding + // the first compile unit child DIE and should reserve + // the memory. + if (m_die_array.empty()) + m_die_array.reserve(GetDebugInfoSize() / 14); + m_die_array.push_back(die); + } + + DWARFDebugInfoEntry* + GetDIEPtr (dw_offset_t die_offset); + + const DWARFDebugInfoEntry* + GetDIEPtrContainingOffset (dw_offset_t die_offset); + + static uint8_t + GetAddressByteSize(const DWARFCompileUnit* cu); + + static uint8_t + GetDefaultAddressSize(); + static void + SetDefaultAddressSize(uint8_t addr_size); + + void * + GetUserData() const + { + return m_user_data; + } + + void + SetUserData(void *d) + { + m_user_data = d; + } + + + void + AddGlobalDIEByIndex (uint32_t die_idx); + + void + AddGlobal (const DWARFDebugInfoEntry* die); + + size_t + GetNumGlobals () const + { + return m_global_die_indexes.size(); + } + + const DWARFDebugInfoEntry * + GetGlobalDIEAtIndex (uint32_t idx) + { + if (idx < m_global_die_indexes.size()) + { + uint32_t die_idx = m_global_die_indexes[idx]; + if (die_idx < m_die_array.size()) + return &m_die_array[die_idx]; + } + return NULL; + } + + void + Index (lldb_private::UniqueCStringMap& name_to_function_die, + lldb_private::UniqueCStringMap& name_to_inlined_die, + lldb_private::UniqueCStringMap& name_to_global_die, + lldb_private::UniqueCStringMap& name_to_type_die); + +protected: + SymbolFileDWARF* m_dwarf2Data; + dw_offset_t m_offset; + uint32_t m_length; + uint16_t m_version; + const DWARFAbbreviationDeclarationSet* + m_abbrevs; + uint8_t m_addr_size; + dw_addr_t m_base_addr; + DWARFDebugInfoEntry::collection + m_die_array; // The compile unit debug information entry item + std::auto_ptr m_aranges_ap; // A table similar to the .debug_aranges table, but this one points to the exact DW_TAG_subprogram DIEs + std::vector m_global_die_indexes; // Indexes to all file level global and static variables + void * m_user_data; +private: + DISALLOW_COPY_AND_ASSIGN (DWARFCompileUnit); +}; + +#endif // SymbolFileDWARF_DWARFCompileUnit_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp new file mode 100644 index 000000000000..60aac74ad861 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp @@ -0,0 +1,56 @@ +//===-- DWARFDIECollection.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDIECollection.h" + +#include + +#include "lldb/Core/Stream.h" + +#include "DWARFDebugInfoEntry.h" + +using namespace lldb_private; +using namespace std; + +bool +DWARFDIECollection::Insert(const DWARFDebugInfoEntry *die) +{ + iterator end_pos = m_dies.end(); + iterator insert_pos = upper_bound(m_dies.begin(), end_pos, die); + if (insert_pos != end_pos && (*insert_pos == die)) + return false; + m_dies.insert(insert_pos, die); + return true; +} + +const DWARFDebugInfoEntry * +DWARFDIECollection::GetDIEPtrAtIndex(uint32_t idx) const +{ + if (idx < m_dies.size()) + return m_dies[idx]; + return NULL; +} + + +size_t +DWARFDIECollection::Size() const +{ + return m_dies.size(); +} + +void +DWARFDIECollection::Dump(Stream *s, const char* title) const +{ + if (title && title[0] != '\0') + s->Printf( "%s\n", title); + const_iterator end_pos = m_dies.end(); + const_iterator pos; + for (pos = m_dies.begin(); pos != end_pos; ++pos) + s->Printf( "0x%8.8x\n", (*pos)->GetOffset()); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h new file mode 100644 index 000000000000..c374a148c6cb --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h @@ -0,0 +1,48 @@ +//===-- DWARFDIECollection.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDIECollection_h_ +#define SymbolFileDWARF_DWARFDIECollection_h_ + +#include "SymbolFileDWARF.h" +#include + +class DWARFDIECollection +{ +public: + DWARFDIECollection() : + m_dies() + { + } + ~DWARFDIECollection() + { + } + + void + Dump(lldb_private::Stream *s, const char* title) const; + + const DWARFDebugInfoEntry* + GetDIEPtrAtIndex(uint32_t idx) const; + + bool + Insert(const DWARFDebugInfoEntry *die); + + size_t + Size() const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_dies; // Ordered list of die offsets +}; + + +#endif // SymbolFileDWARF_DWARFDIECollection_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 000000000000..af6fc8e93e79 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,202 @@ +//===-- DWARFDebugAbbrev.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugAbbrev.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Clear() +//---------------------------------------------------------------------- +void +DWARFAbbreviationDeclarationSet::Clear() +{ + m_idx_offset = 0; + m_decls.clear(); +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Extract() +//---------------------------------------------------------------------- +bool +DWARFAbbreviationDeclarationSet::Extract(const DataExtractor& data, uint32_t* offset_ptr) +{ + const uint32_t begin_offset = *offset_ptr; + m_offset = begin_offset; + Clear(); + DWARFAbbreviationDeclaration abbrevDeclaration; + dw_uleb128_t prev_abbr_code = 0; + while (abbrevDeclaration.Extract(data, offset_ptr)) + { + m_decls.push_back(abbrevDeclaration); + if (m_idx_offset == 0) + m_idx_offset = abbrevDeclaration.Code(); + else + { + if (prev_abbr_code + 1 != abbrevDeclaration.Code()) + m_idx_offset = UINT_MAX; // Out of order indexes, we can't do O(1) lookups... + } + prev_abbr_code = abbrevDeclaration.Code(); + } + return begin_offset != *offset_ptr; +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Dump() +//---------------------------------------------------------------------- +void +DWARFAbbreviationDeclarationSet::Dump(Stream *s) const +{ + std::for_each (m_decls.begin(), m_decls.end(), bind2nd(std::mem_fun_ref(&DWARFAbbreviationDeclaration::Dump),s)); +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration() +//---------------------------------------------------------------------- +const DWARFAbbreviationDeclaration* +DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const +{ + if (m_idx_offset == UINT_MAX) + { + DWARFAbbreviationDeclarationCollConstIter pos; + DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); + for (pos = m_decls.begin(); pos != end; ++pos) + { + if (pos->Code() == abbrCode) + return &(*pos); + } + } + else + { + uint32_t idx = abbrCode - m_idx_offset; + if (idx < m_decls.size()) + return &m_decls[idx]; + } + return NULL; +} + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::AppendAbbrevDeclSequential() +// +// Append an abbreviation declaration with a sequential code for O(n) +// lookups. Handy when creating an DWARFAbbreviationDeclarationSet. +//---------------------------------------------------------------------- +dw_uleb128_t +DWARFAbbreviationDeclarationSet::AppendAbbrevDeclSequential(const DWARFAbbreviationDeclaration& abbrevDecl) +{ + // Get the next abbreviation code based on our current array size + dw_uleb128_t code = m_decls.size()+1; + + // Push the new declaration on the back + m_decls.push_back(abbrevDecl); + + // Update the code for this new declaration + m_decls.back().SetCode(code); + + return code; // return the new abbreviation code! +} + + +//---------------------------------------------------------------------- +// Encode +// +// Encode the abbreviation table onto the end of the buffer provided +// into a byte represenation as would be found in a ".debug_abbrev" +// debug information section. +//---------------------------------------------------------------------- +//void +//DWARFAbbreviationDeclarationSet::Encode(BinaryStreamBuf& debug_abbrev_buf) const +//{ +// DWARFAbbreviationDeclarationCollConstIter pos; +// DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); +// for (pos = m_decls.begin(); pos != end; ++pos) +// pos->Append(debug_abbrev_buf); +// debug_abbrev_buf.Append8(0); +//} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev constructor +//---------------------------------------------------------------------- +DWARFDebugAbbrev::DWARFDebugAbbrev() : + m_abbrevCollMap(), + m_prev_abbr_offset_pos(m_abbrevCollMap.end()) +{ +} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::Parse() +//---------------------------------------------------------------------- +void +DWARFDebugAbbrev::Parse(const DataExtractor& data) +{ + uint32_t offset = 0; + + while (data.ValidOffset(offset)) + { + uint32_t initial_cu_offset = offset; + DWARFAbbreviationDeclarationSet abbrevDeclSet; + + if (abbrevDeclSet.Extract(data, &offset)) + m_abbrevCollMap[initial_cu_offset] = abbrevDeclSet; + else + break; + } + m_prev_abbr_offset_pos = m_abbrevCollMap.end(); +} + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::Dump() +//---------------------------------------------------------------------- +void +DWARFDebugAbbrev::Dump(Stream *s) const +{ + if (m_abbrevCollMap.empty()) + { + s->PutCString("< EMPTY >\n"); + return; + } + + DWARFAbbreviationDeclarationCollMapConstIter pos; + for (pos = m_abbrevCollMap.begin(); pos != m_abbrevCollMap.end(); ++pos) + { + s->Printf("Abbrev table for offset: 0x%8.8x\n", pos->first); + pos->second.Dump(s); + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::GetAbbreviationDeclarationSet() +//---------------------------------------------------------------------- +const DWARFAbbreviationDeclarationSet* +DWARFDebugAbbrev::GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const +{ + DWARFAbbreviationDeclarationCollMapConstIter end = m_abbrevCollMap.end(); + DWARFAbbreviationDeclarationCollMapConstIter pos; + if (m_prev_abbr_offset_pos != end && m_prev_abbr_offset_pos->first == cu_abbr_offset) + return &(m_prev_abbr_offset_pos->second); + else + { + pos = m_abbrevCollMap.find(cu_abbr_offset); + m_prev_abbr_offset_pos = pos; + } + + if (pos != m_abbrevCollMap.end()); + return &(pos->second); + return NULL; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h new file mode 100644 index 000000000000..3185d9895a3c --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h @@ -0,0 +1,74 @@ +//===-- DWARFDebugAbbrev.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugAbbrev_h_ +#define SymbolFileDWARF_DWARFDebugAbbrev_h_ + +#include +#include + +#include "lldb/lldb-private.h" + +#include "DWARFDefines.h" +#include "DWARFAbbreviationDeclaration.h" + +typedef std::vector DWARFAbbreviationDeclarationColl; +typedef DWARFAbbreviationDeclarationColl::iterator DWARFAbbreviationDeclarationCollIter; +typedef DWARFAbbreviationDeclarationColl::const_iterator DWARFAbbreviationDeclarationCollConstIter; + + +class DWARFAbbreviationDeclarationSet +{ +public: + DWARFAbbreviationDeclarationSet() : + m_offset(DW_INVALID_OFFSET), + m_idx_offset(0), + m_decls() + { + } + + DWARFAbbreviationDeclarationSet(dw_offset_t offset, uint32_t idx_offset) : + m_offset(offset), + m_idx_offset(idx_offset), + m_decls() + { + } + + void Clear(); + dw_offset_t GetOffset() const { return m_offset; } + void Dump(lldb_private::Stream *s) const; + bool Extract(const lldb_private::DataExtractor& data, uint32_t* offset_ptr); + //void Encode(BinaryStreamBuf& debug_abbrev_buf) const; + dw_uleb128_t AppendAbbrevDeclSequential(const DWARFAbbreviationDeclaration& abbrevDecl); + + const DWARFAbbreviationDeclaration* GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const; +private: + dw_offset_t m_offset; + uint32_t m_idx_offset; + std::vector m_decls; +}; + +typedef std::map DWARFAbbreviationDeclarationCollMap; +typedef DWARFAbbreviationDeclarationCollMap::iterator DWARFAbbreviationDeclarationCollMapIter; +typedef DWARFAbbreviationDeclarationCollMap::const_iterator DWARFAbbreviationDeclarationCollMapConstIter; + + +class DWARFDebugAbbrev +{ +public: + DWARFDebugAbbrev(); + const DWARFAbbreviationDeclarationSet* GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const; + void Dump(lldb_private::Stream *s) const; + void Parse(const lldb_private::DataExtractor& data); +protected: + DWARFAbbreviationDeclarationCollMap m_abbrevCollMap; + mutable DWARFAbbreviationDeclarationCollMapConstIter m_prev_abbr_offset_pos; +}; + +#endif // SymbolFileDWARF_DWARFDebugAbbrev_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 000000000000..57ef6ba4eb40 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,274 @@ +//===-- DWARFDebugArangeSet.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugArangeSet.h" + +#include +#include "lldb/Core/Stream.h" +#include "SymbolFileDWARF.h" + +using namespace lldb_private; + +DWARFDebugArangeSet::DWARFDebugArangeSet() : + m_offset(DW_INVALID_OFFSET), + m_header(), + m_arange_descriptors() +{ + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; +} + +void +DWARFDebugArangeSet::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; + m_arange_descriptors.clear(); +} + +void +DWARFDebugArangeSet::SetHeader +( + uint16_t version, + uint32_t cu_offset, + uint8_t addr_size, + uint8_t seg_size +) +{ + m_header.version = version; + m_header.cu_offset = cu_offset; + m_header.addr_size = addr_size; + m_header.seg_size = seg_size; +} + +void +DWARFDebugArangeSet::Compact() +{ + if (m_arange_descriptors.empty()) + return; + + // Iterate through all arange descriptors and combine any ranges that + // overlap or have matching boundaries. The m_arange_descriptors are assumed + // to be in ascending order after being built by adding descriptors + // using the AddDescriptor method. + uint32_t i = 0; + while (i + 1 < m_arange_descriptors.size()) + { + if (m_arange_descriptors[i].end_address() >= m_arange_descriptors[i+1].address) + { + // The current range ends at or exceeds the start of the next address range. + // Compute the max end address between the two and use that to make the new + // length. + const dw_addr_t max_end_addr = std::max(m_arange_descriptors[i].end_address(), m_arange_descriptors[i+1].end_address()); + m_arange_descriptors[i].length = max_end_addr - m_arange_descriptors[i].address; + // Now remove the next entry as it was just combined with the previous one. + m_arange_descriptors.erase(m_arange_descriptors.begin()+i+1); + } + else + { + // Discontiguous address range, just proceed to the next one. + ++i; + } + } +} +//---------------------------------------------------------------------- +// Compare function DWARFDebugArangeSet::Descriptor structures +//---------------------------------------------------------------------- +static bool DescriptorLessThan (const DWARFDebugArangeSet::Descriptor& range1, const DWARFDebugArangeSet::Descriptor& range2) +{ + return range1.address < range2.address; +} + +//---------------------------------------------------------------------- +// Add a range descriptor and keep things sorted so we can easily +// compact the ranges before being saved or used. +//---------------------------------------------------------------------- +void +DWARFDebugArangeSet::AddDescriptor(const DWARFDebugArangeSet::Descriptor& range) +{ + if (m_arange_descriptors.empty()) + { + m_arange_descriptors.push_back(range); + return; + } + + DescriptorIter end = m_arange_descriptors.end(); + DescriptorIter pos = lower_bound(m_arange_descriptors.begin(), end, range, DescriptorLessThan); + const dw_addr_t range_end_addr = range.end_address(); + if (pos != end) + { + const dw_addr_t found_end_addr = pos->end_address(); + if (range.address < pos->address) + { + if (range_end_addr < pos->address) + { + // Non-contiguous entries, add this one before the found entry + m_arange_descriptors.insert(pos, range); + } + else if (range_end_addr == pos->address) + { + // The top end of 'range' is the lower end of the entry + // pointed to by 'pos'. We can combine range with the + // entry we found by setting the starting address and + // increasing the length since they don't overlap. + pos->address = range.address; + pos->length += range.length; + } + else + { + // We can combine these two and make sure the largest end + // address is used to make end address. + pos->address = range.address; + pos->length = std::max(found_end_addr, range_end_addr) - pos->address; + } + } + else if (range.address == pos->address) + { + pos->length = std::max(pos->length, range.length); + } + } + else + { + // NOTE: 'pos' points to entry past the end which is ok for insert, + // don't use otherwise!!! + const dw_addr_t max_addr = m_arange_descriptors.back().end_address(); + if (max_addr < range.address) + { + // Non-contiguous entries, add this one before the found entry + m_arange_descriptors.insert(pos, range); + } + else if (max_addr == range.address) + { + m_arange_descriptors.back().length += range.length; + } + else + { + m_arange_descriptors.back().length = std::max(max_addr, range_end_addr) - m_arange_descriptors.back().address; + } + } +} + +bool +DWARFDebugArangeSet::Extract(const DataExtractor &data, uint32_t* offset_ptr) +{ + if (data.ValidOffset(*offset_ptr)) + { + m_arange_descriptors.clear(); + m_offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header consisting of: a + // 4-byte length containing the length of the set of entries for this + // compilation unit, not including the length field itself; a 2-byte + // version identifier containing the value 2 for DWARF Version 2; a + // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer + // containing the size in bytes of an address (or the offset portion of + // an address for segmented addressing) on the target system; and a + // 1-byte unsigned integer containing the size in bytes of a segment + // descriptor on the target system. This header is followed by a series + // of tuples. Each tuple consists of an address and a length, each in + // the size appropriate for an address on the target architecture. + m_header.length = data.GetU32(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.cu_offset = data.GetU32(offset_ptr); + m_header.addr_size = data.GetU8(offset_ptr); + m_header.seg_size = data.GetU8(offset_ptr); + + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - m_offset; + const uint32_t tuple_size = m_header.addr_size << 1; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = m_offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + assert(sizeof(arangeDescriptor.address) == sizeof(arangeDescriptor.length)); + assert(sizeof(arangeDescriptor.address) >= m_header.addr_size); + + while (data.ValidOffset(*offset_ptr)) + { + arangeDescriptor.address = data.GetMaxU64(offset_ptr, m_header.addr_size); + arangeDescriptor.length = data.GetMaxU64(offset_ptr, m_header.addr_size); + + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.address || arangeDescriptor.length) + m_arange_descriptors.push_back(arangeDescriptor); + else + break; // We are done if we get a zero address and length + } + + return !m_arange_descriptors.empty(); + } + return false; +} + + +dw_offset_t +DWARFDebugArangeSet::GetOffsetOfNextEntry() const +{ + return m_offset + m_header.length + 4; +} + + +void +DWARFDebugArangeSet::Dump(Stream *s) const +{ + s->Printf("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", + m_header.length ,m_header.version, m_header.cu_offset, m_header.addr_size, m_header.seg_size); + + const uint32_t hex_width = m_header.addr_size * 2; + DescriptorConstIter pos; + DescriptorConstIter end = m_arange_descriptors.end(); + for (pos = m_arange_descriptors.begin(); pos != end; ++pos) + s->Printf("[0x%*.*llx - 0x%*.*llx)\n", + hex_width, hex_width, pos->address, + hex_width, hex_width, pos->end_address()); +} + + +class DescriptorContainsAddress +{ +public: + DescriptorContainsAddress (dw_addr_t address) : m_address(address) {} + bool operator() (const DWARFDebugArangeSet::Descriptor& desc) const + { + return (m_address >= desc.address) && (m_address < (desc.address + desc.length)); + } + private: + const dw_addr_t m_address; +}; + +dw_offset_t +DWARFDebugArangeSet::FindAddress(dw_addr_t address) const +{ + DescriptorConstIter end = m_arange_descriptors.end(); + DescriptorConstIter pos = std::find_if( m_arange_descriptors.begin(), end, // Range + DescriptorContainsAddress(address));// Predicate + if (pos != end) + return m_header.cu_offset; + + return DW_INVALID_OFFSET; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h new file mode 100644 index 000000000000..fc1e391aeb47 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h @@ -0,0 +1,70 @@ +//===-- DWARFDebugArangeSet.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugArangeSet_h_ +#define SymbolFileDWARF_DWARFDebugArangeSet_h_ + +#include "SymbolFileDWARF.h" +#include + +class SymbolFileDWARF; + +class DWARFDebugArangeSet +{ +public: + typedef struct HeaderTag + { + uint32_t length; // The total length of the entries for that set, not including the length field itself. + uint16_t version; // The DWARF version number + uint32_t cu_offset; // The offset from the beginning of the .debug_info section of the compilation unit entry referenced by the table. + uint8_t addr_size; // The size in bytes of an address on the target architecture. For segmented addressing, this is the size of the offset portion of the address + uint8_t seg_size; // The size in bytes of a segment descriptor on the target architecture. If the target system uses a flat address space, this value is 0. + } Header; + + typedef struct DescriptorTag + { + dw_addr_t address; + dw_addr_t length; + dw_addr_t end_address() const { return address + length; } + } Descriptor; + + + DWARFDebugArangeSet(); + void Clear(); + void SetOffset(uint32_t offset) { m_offset = offset; } + void SetHeader(uint16_t version, uint32_t cu_offset, uint8_t addr_size, uint8_t seg_size); + void AddDescriptor(const DWARFDebugArangeSet::Descriptor& range); + void Compact(); + bool Extract(const lldb_private::DataExtractor &data, uint32_t* offset_ptr); + void Dump(lldb_private::Stream *s) const; + dw_offset_t GetCompileUnitDIEOffset() const { return m_header.cu_offset; } + dw_offset_t GetOffsetOfNextEntry() const; + dw_offset_t FindAddress(dw_addr_t address) const; + uint32_t NumDescriptors() const { return m_arange_descriptors.size(); } + const Header& GetHeader() const { return m_header; } + const Descriptor* GetDescriptor(uint32_t i) const + { + if (i < m_arange_descriptors.size()) + return &m_arange_descriptors[i]; + return NULL; + } + + +protected: + typedef std::vector DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + + uint32_t m_offset; + Header m_header; + DescriptorColl m_arange_descriptors; +}; + +#endif // SymbolFileDWARF_DWARFDebugArangeSet_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp new file mode 100644 index 000000000000..a3213e080c3a --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,343 @@ +//===-- DWARFDebugAranges.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugAranges.h" + +#include + +#include + +#include "lldb/Core/Stream.h" + +#include "SymbolFileDWARF.h" +#include "DWARFDebugInfo.h" +#include "DWARFCompileUnit.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DWARFDebugAranges::DWARFDebugAranges() : + m_aranges() +{ +} + + +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool RangeLessThan (const DWARFDebugAranges::Range& range1, const DWARFDebugAranges::Range& range2) +{ +// printf("RangeLessThan -- 0x%8.8x < 0x%8.8x ? %d\n", range1.lo_pc, range1.lo_pc, range1.lo_pc < range2.lo_pc); + return range1.lo_pc < range2.lo_pc; +} + +//---------------------------------------------------------------------- +// CountArangeDescriptors +//---------------------------------------------------------------------- +class CountArangeDescriptors +{ +public: + CountArangeDescriptors (uint32_t& count_ref) : count(count_ref) + { +// printf("constructor CountArangeDescriptors()\n"); + } + void operator() (const DWARFDebugArangeSet& set) + { + count += set.NumDescriptors(); + } + uint32_t& count; +}; + +//---------------------------------------------------------------------- +// AddArangeDescriptors +//---------------------------------------------------------------------- +class AddArangeDescriptors +{ +public: + AddArangeDescriptors (DWARFDebugAranges::RangeColl& ranges) : range_collection(ranges) {} + void operator() (const DWARFDebugArangeSet& set) + { + const DWARFDebugArangeSet::Descriptor* arange_desc_ptr; + DWARFDebugAranges::Range range; + range.offset = set.GetCompileUnitDIEOffset(); + + for (uint32_t i=0; arange_desc_ptr = set.GetDescriptor(i); ++i) + { + range.lo_pc = arange_desc_ptr->address; + range.hi_pc = arange_desc_ptr->address + arange_desc_ptr->length; + + // Insert each item in increasing address order so binary searching + // can later be done! + DWARFDebugAranges::RangeColl::iterator insert_pos = lower_bound(range_collection.begin(), range_collection.end(), range, RangeLessThan); + range_collection.insert(insert_pos, range); + } + } + DWARFDebugAranges::RangeColl& range_collection; +}; + +//---------------------------------------------------------------------- +// PrintRange +//---------------------------------------------------------------------- +static void PrintRange(const DWARFDebugAranges::Range& range) +{ + // Cast the address values in case the address type is compiled as 32 bit + printf("0x%8.8x: [0x%8.8llx - 0x%8.8llx)\n", range.offset, (uint64_t)range.lo_pc, (uint64_t)range.hi_pc); +} + +//---------------------------------------------------------------------- +// Extract +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::Extract(const DataExtractor &debug_aranges_data) +{ + if (debug_aranges_data.ValidOffset(0)) + { + uint32_t offset = 0; + + typedef std::vector SetCollection; + typedef SetCollection::const_iterator SetCollectionIter; + SetCollection sets; + + DWARFDebugArangeSet set; + Range range; + while (set.Extract(debug_aranges_data, &offset)) + sets.push_back(set); + + uint32_t count = 0; + + for_each(sets.begin(), sets.end(), CountArangeDescriptors(count)); + + if (count > 0) + { + m_aranges.reserve(count); + AddArangeDescriptors range_adder(m_aranges); + for_each(sets.begin(), sets.end(), range_adder); + } + + // puts("\n\nDWARFDebugAranges list is:\n"); + // for_each(m_aranges.begin(), m_aranges.end(), PrintRange); + } + return false; +} + +//---------------------------------------------------------------------- +// Generate +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::Generate(SymbolFileDWARF* dwarf2Data) +{ + Clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + if (cu) + cu->DIE()->BuildAddressRangeTable(dwarf2Data, cu, this); + } + } + return !IsEmpty(); +} + + +void +DWARFDebugAranges::Print() const +{ + puts("\n\nDWARFDebugAranges address range list is:\n"); + for_each(m_aranges.begin(), m_aranges.end(), PrintRange); +} + + +void +DWARFDebugAranges::Range::Dump(Stream *s) const +{ + s->Printf("{0x%8.8x}: [0x%8.8llx - 0x%8.8llx)\n", offset, lo_pc, hi_pc); +} + +//---------------------------------------------------------------------- +// Dump +//---------------------------------------------------------------------- +void +DWARFDebugAranges::Dump(SymbolFileDWARF* dwarf2Data, Stream *s) +{ + const DataExtractor &debug_aranges_data = dwarf2Data->get_debug_aranges_data(); + if (debug_aranges_data.ValidOffset(0)) + { + uint32_t offset = 0; + + DWARFDebugArangeSet set; + while (set.Extract(debug_aranges_data, &offset)) + set.Dump(s); + } + else + s->PutCString("< EMPTY >\n"); +} + + +//---------------------------------------------------------------------- +// AppendDebugRanges +//---------------------------------------------------------------------- +//void +//DWARFDebugAranges::AppendDebugRanges(BinaryStreamBuf& debug_ranges, dw_addr_t cu_base_addr, uint32_t addr_size) const +//{ +// if (!m_aranges.empty()) +// { +// RangeCollIterator end = m_aranges.end(); +// RangeCollIterator pos; +// RangeCollIterator lo_pos = end; +// for (pos = m_aranges.begin(); pos != end; ++pos) +// { +// if (lo_pos == end) +// lo_pos = pos; +// +// RangeCollIterator next = pos + 1; +// if (next != end) +// { +// // Check to see if we can combine two consecutive ranges? +// if (pos->hi_pc == next->lo_pc) +// continue; // We can combine them! +// } +// +// if (cu_base_addr == 0 || cu_base_addr == DW_INVALID_ADDRESS) +// { +// debug_ranges.AppendMax64(lo_pos->lo_pc, addr_size); +// debug_ranges.AppendMax64(pos->hi_pc, addr_size); +// } +// else +// { +// assert(lo_pos->lo_pc >= cu_base_addr); +// assert(pos->hi_pc >= cu_base_addr); +// debug_ranges.AppendMax64(lo_pos->lo_pc - cu_base_addr, addr_size); +// debug_ranges.AppendMax64(pos->hi_pc - cu_base_addr, addr_size); +// } +// +// // Reset the low part of the next address range +// lo_pos = end; +// } +// } +// // Terminate the .debug_ranges with two zero addresses +// debug_ranges.AppendMax64(0, addr_size); +// debug_ranges.AppendMax64(0, addr_size); +// +//} +// +//---------------------------------------------------------------------- +// ArangeSetContainsAddress +//---------------------------------------------------------------------- +class ArangeSetContainsAddress +{ +public: + ArangeSetContainsAddress (dw_addr_t the_address) : address(the_address), offset(DW_INVALID_OFFSET) {} + bool operator() (const DWARFDebugArangeSet& set) + { + offset = set.FindAddress(address); + return (offset != DW_INVALID_OFFSET); + } + const dw_addr_t address; + dw_offset_t offset; +}; + + +//---------------------------------------------------------------------- +// InsertRange +//---------------------------------------------------------------------- +void +DWARFDebugAranges::InsertRange(dw_offset_t offset, dw_addr_t low_pc, dw_addr_t high_pc) +{ + // Insert each item in increasing address order so binary searching + // can later be done! + DWARFDebugAranges::Range range(low_pc, high_pc, offset); + InsertRange(range); +} + +//---------------------------------------------------------------------- +// InsertRange +//---------------------------------------------------------------------- +void +DWARFDebugAranges::InsertRange(const DWARFDebugAranges::Range& range) +{ + // Insert each item in increasing address order so binary searching + // can later be done! + RangeColl::iterator insert_pos = lower_bound(m_aranges.begin(), m_aranges.end(), range, RangeLessThan); + m_aranges.insert(insert_pos, range); +} + + +//---------------------------------------------------------------------- +// FindAddress +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugAranges::FindAddress(dw_addr_t address) const +{ + if ( !m_aranges.empty() ) + { + DWARFDebugAranges::Range range(address); + DWARFDebugAranges::RangeCollIterator begin = m_aranges.begin(); + DWARFDebugAranges::RangeCollIterator end = m_aranges.end(); + DWARFDebugAranges::RangeCollIterator pos = lower_bound(begin, end, range, RangeLessThan); + + if ((pos != end) && (pos->lo_pc <= address && address < pos->hi_pc)) + { + // printf("FindAddress(1) found 0x%8.8x in compile unit: 0x%8.8x\n", address, pos->offset); + return pos->offset; + } + else if (pos != begin) + { + --pos; + if ((pos->lo_pc <= address) && (address < pos->hi_pc)) + { + // printf("FindAddress(2) found 0x%8.8x in compile unit: 0x%8.8x\n", address, pos->offset); + return (*pos).offset; + } + } + } + return DW_INVALID_OFFSET; +} + +//---------------------------------------------------------------------- +// AllRangesAreContiguous +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::AllRangesAreContiguous(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const +{ + if (m_aranges.empty()) + return false; + + DWARFDebugAranges::RangeCollIterator begin = m_aranges.begin(); + DWARFDebugAranges::RangeCollIterator end = m_aranges.end(); + DWARFDebugAranges::RangeCollIterator pos; + dw_addr_t next_addr = 0; + + for (pos = begin; pos != end; ++pos) + { + if ((pos != begin) && (pos->lo_pc != next_addr)) + return false; + next_addr = pos->hi_pc; + } + lo_pc = m_aranges.front().lo_pc; // We checked for empty at the start of function so front() will be valid + hi_pc = m_aranges.back().hi_pc; // We checked for empty at the start of function so back() will be valid + return true; +} + +bool +DWARFDebugAranges::GetMaxRange(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const +{ + if (m_aranges.empty()) + return false; + + lo_pc = m_aranges.front().lo_pc; // We checked for empty at the start of function so front() will be valid + hi_pc = m_aranges.back().hi_pc; // We checked for empty at the start of function so back() will be valid + return true; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h new file mode 100644 index 000000000000..f3db949bf267 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h @@ -0,0 +1,98 @@ +//===-- DWARFDebugAranges.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugAranges_h_ +#define SymbolFileDWARF_DWARFDebugAranges_h_ + +#include "DWARFDebugArangeSet.h" +#include + +class SymbolFileDWARF; + +class DWARFDebugAranges +{ +public: + struct Range + { + Range( + dw_addr_t _lo_pc = DW_INVALID_ADDRESS, + dw_addr_t _hi_pc = DW_INVALID_ADDRESS, + dw_offset_t _offset = DW_INVALID_OFFSET) : + lo_pc(_lo_pc), + hi_pc(_hi_pc), + offset(_offset) + { + } + + void Clear() + { + lo_pc = hi_pc = DW_INVALID_ADDRESS; + offset = DW_INVALID_OFFSET; + } + + bool ValidRange() const + { + return hi_pc > lo_pc; + } + + bool Contains(const Range& range) const + { + return lo_pc <= range.lo_pc && range.hi_pc <= hi_pc; + } + + void Dump(lldb_private::Stream *s) const; + dw_addr_t lo_pc; // Start of address range + dw_addr_t hi_pc; // End of address range (not including this address) + dw_offset_t offset; // Offset of the compile unit or die + }; + + DWARFDebugAranges(); + + void Clear() { m_aranges.clear(); } + bool AllRangesAreContiguous(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const; + bool GetMaxRange(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const; + bool Extract(const lldb_private::DataExtractor &debug_aranges_data); + bool Generate(SymbolFileDWARF* dwarf2Data); + void InsertRange(dw_offset_t cu_offset, dw_addr_t low_pc, dw_addr_t high_pc); + void InsertRange(const DWARFDebugAranges::Range& range); + const Range* RangeAtIndex(uint32_t idx) const + { + if (idx < m_aranges.size()) + return &m_aranges[idx]; + return NULL; + } + void Print() const; + dw_offset_t FindAddress(dw_addr_t address) const; + bool IsEmpty() const { return m_aranges.empty(); } + void Dump(lldb_private::Stream *s); + uint32_t NumRanges() const + { + return m_aranges.size(); + } + + dw_offset_t OffsetAtIndex(uint32_t idx) const + { + if (idx < m_aranges.size()) + return m_aranges[idx].offset; + return DW_INVALID_OFFSET; + } +// void AppendDebugRanges(BinaryStreamBuf& debug_ranges, dw_addr_t cu_base_addr, uint32_t addr_size) const; + + static void Dump(SymbolFileDWARF* dwarf2Data, lldb_private::Stream *s); + + typedef std::vector RangeColl; + typedef RangeColl::const_iterator RangeCollIterator; + +protected: + + RangeColl m_aranges; +}; + + +#endif // SymbolFileDWARF_DWARFDebugAranges_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp new file mode 100644 index 000000000000..fcb0ccf11898 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -0,0 +1,1206 @@ +//===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARF.h" + +#include +#include + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" + +#include "DWARFDebugInfo.h" +#include "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFFormValue.h" + +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DWARFDebugInfo::DWARFDebugInfo() : + m_dwarf2Data(NULL), + m_compile_units() +{ +} + +//---------------------------------------------------------------------- +// SetDwarfData +//---------------------------------------------------------------------- +void +DWARFDebugInfo::SetDwarfData(SymbolFileDWARF* dwarf2Data) +{ + m_dwarf2Data = dwarf2Data; + m_compile_units.clear(); +} + +//---------------------------------------------------------------------- +// BuildDIEAddressRangeTable +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::BuildFunctionAddressRangeTable(DWARFDebugAranges* debug_aranges) +{ + const uint32_t num_compile_units = GetNumCompileUnits(); + uint32_t idx; + for (idx = 0; idx < num_compile_units; ++idx) + { + DWARFCompileUnit* cu = GetCompileUnitAtIndex (idx); + if (cu) + { + cu->DIE()->BuildFunctionAddressRangeTable(m_dwarf2Data, cu, debug_aranges); + } + } + return !debug_aranges->IsEmpty(); +} + +//---------------------------------------------------------------------- +// LookupAddress +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::LookupAddress +( + const dw_addr_t address, + const dw_offset_t hint_die_offset, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die +) +{ + + if (hint_die_offset != DW_INVALID_OFFSET) + cu_sp = GetCompileUnit(hint_die_offset); + else + { + // Get a non const version of the address ranges + DWARFDebugAranges* debug_aranges = ((SymbolFileDWARF*)m_dwarf2Data)->DebugAranges(); + + if (debug_aranges != NULL) + { + // If we have an empty address ranges section, lets build a sorted + // table ourselves by going through all of the debug information so we + // can do quick subsequent searches. + + if (debug_aranges->IsEmpty()) + { + const uint32_t num_compile_units = GetNumCompileUnits(); + uint32_t idx; + for (idx = 0; idx < num_compile_units; ++idx) + { + DWARFCompileUnit* cu = GetCompileUnitAtIndex(idx); + if (cu) + cu->DIE()->BuildAddressRangeTable(m_dwarf2Data, cu, debug_aranges); + } + } + cu_sp = GetCompileUnit(debug_aranges->FindAddress(address)); + } + } + + if (cu_sp.get()) + { + if (cu_sp->LookupAddress(address, function_die, block_die)) + return true; + cu_sp.reset(); + } + else + { + // The hint_die_offset may have been a pointer to the actual item that + // we are looking for + DWARFDebugInfoEntry* die_ptr = GetDIEPtr(hint_die_offset, &cu_sp); + if (die_ptr) + { + if (cu_sp.get()) + { + if (function_die || block_die) + return die_ptr->LookupAddress(address, m_dwarf2Data, cu_sp.get(), function_die, block_die); + + // We only wanted the compile unit that contained this address + return true; + } + } + } + return false; +} + + +void +DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() +{ + if (m_compile_units.empty()) + { + if (m_dwarf2Data != NULL) + { + uint32_t offset = 0; + const DataExtractor &debug_info_data = m_dwarf2Data->get_debug_info_data(); + while (debug_info_data.ValidOffset(offset)) + { + DWARFCompileUnitSP cu_sp(new DWARFCompileUnit(m_dwarf2Data)); + // Out of memory? + if (cu_sp.get() == NULL) + break; + + if (cu_sp->Extract(debug_info_data, &offset) == false) + break; + + m_compile_units.push_back(cu_sp); + + offset = cu_sp->GetNextCompileUnitOffset(); + } + } + } +} + +uint32_t +DWARFDebugInfo::GetNumCompileUnits() +{ + ParseCompileUnitHeadersIfNeeded(); + return m_compile_units.size(); +} + +DWARFCompileUnit* +DWARFDebugInfo::GetCompileUnitAtIndex(uint32_t idx) +{ + DWARFCompileUnit* cu = NULL; + if (idx < GetNumCompileUnits()) + cu = m_compile_units[idx].get(); + return cu; +} + +static bool CompileUnitOffsetLessThan (const DWARFCompileUnitSP& a, const DWARFCompileUnitSP& b) +{ + return a->GetOffset() < b->GetOffset(); +} + + +static int +CompareDWARFCompileUnitSPOffset (const void *key, const void *arrmem) +{ + const dw_offset_t key_cu_offset = *(dw_offset_t*) key; + const dw_offset_t cu_offset = ((DWARFCompileUnitSP *)arrmem)->get()->GetOffset(); + if (key_cu_offset < cu_offset) + return -1; + if (key_cu_offset > cu_offset) + return 1; + return 0; +} + +DWARFCompileUnitSP +DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr) +{ + DWARFCompileUnitSP cu_sp; + uint32_t cu_idx = DW_INVALID_INDEX; + if (cu_offset != DW_INVALID_OFFSET) + { + ParseCompileUnitHeadersIfNeeded(); + + DWARFCompileUnitSP* match = (DWARFCompileUnitSP*)bsearch(&cu_offset, &m_compile_units[0], m_compile_units.size(), sizeof(DWARFCompileUnitSP), CompareDWARFCompileUnitSPOffset); + if (match) + { + cu_sp = *match; + cu_idx = match - &m_compile_units[0]; + } + } + if (idx_ptr) + *idx_ptr = cu_idx; + return cu_sp; +} + +DWARFCompileUnitSP +DWARFDebugInfo::GetCompileUnitContainingDIE(dw_offset_t die_offset) +{ + DWARFCompileUnitSP cu_sp; + if (die_offset != DW_INVALID_OFFSET) + { + ParseCompileUnitHeadersIfNeeded(); + + CompileUnitColl::const_iterator end_pos = m_compile_units.end(); + CompileUnitColl::const_iterator pos; + + for (pos = m_compile_units.begin(); pos != end_pos; ++pos) + { + dw_offset_t cu_start_offset = (*pos)->GetOffset(); + dw_offset_t cu_end_offset = (*pos)->GetNextCompileUnitOffset(); + if (cu_start_offset <= die_offset && die_offset < cu_end_offset) + { + cu_sp = *pos; + break; + } + } + } + return cu_sp; +} + +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) +{ + return die1.GetOffset() < die2.GetOffset(); +} + + +//---------------------------------------------------------------------- +// GetDIE() +// +// Get the DIE (Debug Information Entry) with the specified offset. +//---------------------------------------------------------------------- +DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) +{ + DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); + if (cu_sp_ptr) + *cu_sp_ptr = cu_sp; + if (cu_sp.get()) + return cu_sp->GetDIEPtr(die_offset); + return NULL; // Not found in any compile units +} + +const DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) +{ + DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); + if (cu_sp_ptr) + *cu_sp_ptr = cu_sp; + if (cu_sp.get()) + return cu_sp->GetDIEPtrContainingOffset(die_offset); + + return NULL; // Not found in any compile units + +} + +//---------------------------------------------------------------------- +// DWARFDebugInfo_ParseCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets parses all compile units and DIE's into an internate +// representation for further modification. +//---------------------------------------------------------------------- + +static dw_offset_t +DWARFDebugInfo_ParseCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + DWARFDebugInfo* debug_info = (DWARFDebugInfo*)userData; + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + cu->AddDIE(*die); + } + else if (cu) + { + debug_info->AddCompileUnit(cu_sp); + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// AddCompileUnit +//---------------------------------------------------------------------- +void +DWARFDebugInfo::AddCompileUnit(DWARFCompileUnitSP& cu) +{ + m_compile_units.push_back(cu); +} + +/* +void +DWARFDebugInfo::AddDIE(DWARFDebugInfoEntry& die) +{ + m_die_array.push_back(die); +} +*/ + + + + +//---------------------------------------------------------------------- +// Parse +// +// Parses the .debug_info section and uses the .debug_abbrev section +// and various other sections in the SymbolFileDWARF class and calls the +// supplied callback function each time a compile unit header, or debug +// information entry is successfully parsed. This function can be used +// for different tasks such as parsing the file contents into a +// structured data, dumping, verifying and much more. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Parse(SymbolFileDWARF* dwarf2Data, Callback callback, void* userData) +{ + if (dwarf2Data) + { + uint32_t offset = 0; + uint32_t depth = 0; + DWARFCompileUnitSP cu(new DWARFCompileUnit(dwarf2Data)); + if (cu.get() == NULL) + return; + DWARFDebugInfoEntry die; + + while (cu->Extract(dwarf2Data->get_debug_info_data(), &offset)) + { + const dw_offset_t next_cu_offset = cu->GetNextCompileUnitOffset(); + + depth = 0; + // Call the callback funtion with no DIE pointer for the compile unit + // and get the offset that we are to continue to parse from + offset = callback(dwarf2Data, cu, NULL, offset, depth, userData); + + // Make sure we are within our compile unit + if (offset < next_cu_offset) + { + // We are in our compile unit, parse starting at the offset + // we were told to parse + bool done = false; + while (!done && die.Extract(dwarf2Data, cu.get(), &offset)) + { + // Call the callback funtion with DIE pointer that falls within the compile unit + offset = callback(dwarf2Data, cu, &die, offset, depth, userData); + + if (die.IsNULL()) + { + if (depth) + --depth; + else + done = true; // We are done with this compile unit! + } + else if (die.HasChildren()) + ++depth; + } + } + + // Make sure the offset returned is valid, and if not stop parsing. + // Returning DW_INVALID_OFFSET from this callback is a good way to end + // all parsing + if (!dwarf2Data->get_debug_info_data().ValidOffset(offset)) + break; + + // See if during the callback anyone retained a copy of the compile + // unit other than ourselves and if so, let whomever did own the object + // and create a new one for our own use! + if (!cu.unique()) + cu.reset(new DWARFCompileUnit(dwarf2Data)); + + + // Make sure we start on a propper + offset = next_cu_offset; + } + } +} + +/* +typedef struct AddressRangeTag +{ + dw_addr_t lo_pc; + dw_addr_t hi_pc; + dw_offset_t die_offset; +} AddressRange; +*/ +struct DIERange +{ + DIERange() : + range(), + lo_die_offset(), + hi_die_offset() + { + } + + DWARFDebugAranges::Range range; + dw_offset_t lo_die_offset; + dw_offset_t hi_die_offset; +}; + +typedef struct DwarfStat +{ + DwarfStat() : count(0), byte_size(0) {} + uint32_t count; + uint32_t byte_size; +} DwarfStat; + +typedef map DwarfAttrStatMap; + +typedef struct DIEStat +{ + DIEStat() : count(0), byte_size(0), attr_stats() {} + uint32_t count; + uint32_t byte_size; + DwarfAttrStatMap attr_stats; +} DIEStat; + +typedef map DIEStatMap; +struct VerifyInfo +{ + VerifyInfo(Stream* the_strm) : + strm(the_strm), + die_ranges(), + addr_range_errors(0), + sibling_errors(0), + die_stats() + { + } + + Stream* strm; + vector die_ranges; + uint32_t addr_range_errors; + uint32_t sibling_errors; + DIEStatMap die_stats; + + DISALLOW_COPY_AND_ASSIGN(VerifyInfo); + +}; + + +//---------------------------------------------------------------------- +// VerifyCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function will verify the DWARF information is well formed by +// making sure that any DW_TAG_compile_unit tags that have valid address +// ranges (DW_AT_low_pc and DW_AT_high_pc) have no gaps in the address +// ranges of it contained DW_TAG_subprogram tags. Also the sibling chain +// and relationships are verified to make sure nothing gets hosed up +// when dead stripping occurs. +//---------------------------------------------------------------------- + +static dw_offset_t +VerifyCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + VerifyInfo* verifyInfo = (VerifyInfo*)userData; + + const DWARFCompileUnit* cu = cu_sp.get(); + Stream *s = verifyInfo->strm; + bool verbose = s->GetVerbose(); + if (die) + { + // die->Dump(dwarf2Data, cu, f); + const DWARFAbbreviationDeclaration* abbrevDecl = die->GetAbbreviationDeclarationPtr(); + // We have a DIE entry + if (abbrevDecl) + { + const dw_offset_t die_offset = die->GetOffset(); + const dw_offset_t sibling = die->GetAttributeValueAsReference(dwarf2Data, cu, DW_AT_sibling, DW_INVALID_OFFSET); + + if (sibling != DW_INVALID_OFFSET) + { + if (sibling <= next_offset) + { + if (verifyInfo->sibling_errors++ == 0) + s->Printf("ERROR\n"); + s->Printf(" 0x%8.8x: sibling attribyte (0x%8.8x) in this die is not valid: it is less than this DIE or some of its contents.\n", die->GetOffset(), sibling); + } + else if (sibling > verifyInfo->die_ranges.back().hi_die_offset) + { + if (verifyInfo->sibling_errors++ == 0) + s->Printf("ERROR\n"); + s->Printf(" 0x%8.8x: sibling attribute (0x%8.8x) in this DIE is not valid: it is greater than the end of the parent scope.\n", die->GetOffset(), sibling); + } + } + + if ((die_offset < verifyInfo->die_ranges.back().lo_die_offset) || (die_offset >= verifyInfo->die_ranges.back().hi_die_offset)) + { + if (verifyInfo->sibling_errors++ == 0) + s->Printf("ERROR\n"); + s->Printf(" 0x%8.8x: DIE offset is not within the parent DIE range {0x%8.8x}: (0x%8.8x - 0x%8.8x)\n", + die->GetOffset(), + verifyInfo->die_ranges.back().range.offset, + verifyInfo->die_ranges.back().lo_die_offset, + verifyInfo->die_ranges.back().hi_die_offset); + + } + + dw_tag_t tag = abbrevDecl->Tag(); + + // Keep some stats on this DWARF file + verifyInfo->die_stats[tag].count++; + verifyInfo->die_stats[tag].byte_size += (next_offset - die->GetOffset()); + + if (verbose) + { + DIEStat& tag_stat = verifyInfo->die_stats[tag]; + + const DataExtractor& debug_info = dwarf2Data->get_debug_info_data(); + + dw_offset_t offset = die->GetOffset(); + // Skip the abbreviation code so we are at the data for the attributes + debug_info.Skip_LEB128(&offset); + + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + dw_attr_t attr; + dw_form_t form; + for (uint32_t idx = 0; idx < numAttributes; ++idx) + { + dw_offset_t start_offset = offset; + abbrevDecl->GetAttrAndFormByIndexUnchecked(idx, attr, form); + DWARFFormValue::SkipValue(form, debug_info, &offset, cu); + + if (tag_stat.attr_stats.find(attr) == tag_stat.attr_stats.end()) + { + tag_stat.attr_stats[attr].count = 0; + tag_stat.attr_stats[attr].byte_size = 0; + } + + tag_stat.attr_stats[attr].count++; + tag_stat.attr_stats[attr].byte_size += offset - start_offset; + } + } + + DWARFDebugAranges::Range range; + range.offset = die->GetOffset(); + + switch (tag) + { + case DW_TAG_compile_unit: + // Check for previous subroutines that were within a previous + // + // VerifyAddressRangesForCU(verifyInfo); + // Remember which compile unit we are dealing with so we can verify + // the address ranges within it (if any) are contiguous. The DWARF + // spec states that if a compile unit TAG has high and low PC + // attributes, there must be no gaps in the address ranges of it's + // contained subtroutines. If there are gaps, the high and low PC + // must not be in the DW_TAG_compile_unit's attributes. Errors like + // this can crop up when optimized code is dead stripped and the debug + // information isn't properly fixed up for output. + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (range.lo_pc != DW_INVALID_ADDRESS) + { + range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (s->GetVerbose()) + { + s->Printf("\n CU "); + range.Dump(s); + } + } + else + { + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_entry_pc, DW_INVALID_ADDRESS); + } + break; + + case DW_TAG_subprogram: + // If the DW_TAG_compile_unit that contained this function had a + // valid address range, add all of the valid subroutine address + // ranges to a collection of addresses which will be sorted + // and verified right before the next DW_TAG_compile_unit is + // processed to make sure that there are no gaps in the address + // range. + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (range.lo_pc != DW_INVALID_ADDRESS) + { + range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (range.hi_pc != DW_INVALID_ADDRESS) + { + range.offset = die->GetOffset(); + bool valid = range.ValidRange(); + if (!valid || s->GetVerbose()) + { + s->Printf("\n FUNC "); + range.Dump(s); + if (!valid) + { + ++verifyInfo->addr_range_errors; + s->Printf(" ERROR: Invalid address range for function."); + } + } + + // Only add to our subroutine ranges if our compile unit has a valid address range + // if (valid && verifyInfo->die_ranges.size() >= 2 && verifyInfo->die_ranges[1].range.ValidRange()) + // verifyInfo->subroutine_ranges.InsertRange(range); + } + } + break; + + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + { + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (range.lo_pc != DW_INVALID_ADDRESS) + { + range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (range.hi_pc != DW_INVALID_ADDRESS) + { + range.offset = die->GetOffset(); + bool valid = range.ValidRange(); + if (!valid || s->GetVerbose()) + { + s->Printf("\n BLCK "); + range.Dump(s); + if (!valid) + { + ++verifyInfo->addr_range_errors; + s->Printf(" ERROR: Invalid address range for block or inlined subroutine."); + } + } + } + } + } + break; + } + + if (range.ValidRange() && verifyInfo->die_ranges.back().range.ValidRange()) + { + if (!verifyInfo->die_ranges.back().range.Contains(range)) + { + ++verifyInfo->addr_range_errors; + s->Printf("\n "); + range.Dump(s); + s->Printf(" ERROR: Range is not in parent"); + verifyInfo->die_ranges.back().range.Dump(s); + } + } + + if (die->HasChildren()) + { + // Keep tabs on the valid address ranges for the current item to make + // sure that it all fits (make sure the sibling offsets got fixed up + // correctly if any functions were dead stripped). + DIERange die_range; + die_range.range = range; + die_range.lo_die_offset = next_offset; + die_range.hi_die_offset = sibling; + if (die_range.hi_die_offset == DW_INVALID_OFFSET) + die_range.hi_die_offset = verifyInfo->die_ranges.back().hi_die_offset; + verifyInfo->die_ranges.push_back(die_range); + } + } + else + { + // NULL entry + verifyInfo->die_ranges.pop_back(); + } + } + else + { + // cu->Dump(ostrm_ptr); // Dump the compile unit for the DIE + // We have a new comile unit header + verifyInfo->die_ranges.clear(); + DIERange die_range; + die_range.range.offset = cu->GetOffset(); + die_range.lo_die_offset = next_offset; + die_range.hi_die_offset = cu->GetNextCompileUnitOffset(); + verifyInfo->die_ranges.push_back(die_range); + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + + +class CompareDIEStatSizes +{ +public: + bool operator() (const DIEStatMap::const_iterator& pos1, const DIEStatMap::const_iterator& pos2) const + { + return pos1->second.byte_size <= pos2->second.byte_size; + } +}; + +class CompareAttrDIEStatSizes +{ +public: + bool operator() (const DwarfAttrStatMap::const_iterator& pos1, const DwarfAttrStatMap::const_iterator& pos2) const + { + return pos1->second.byte_size <= pos2->second.byte_size; + } +}; + +//---------------------------------------------------------------------- +// Verify +// +// Verifies the DWARF information is valid. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Verify(Stream *s, SymbolFileDWARF* dwarf2Data) +{ + s->Printf("Verifying Compile Unit Header chain....."); + VerifyInfo verifyInfo(s); + verifyInfo.addr_range_errors = 0; + verifyInfo.sibling_errors = 0; + + bool verbose = s->GetVerbose(); + + uint32_t offset = 0; + if (verbose) + s->EOL(); +// vector valid_cu_offsets; + DWARFCompileUnit cu (dwarf2Data); + bool success = true; + while ( success && dwarf2Data->get_debug_info_data().ValidOffset(offset+cu.Size()) ) + { + success = cu.Extract (dwarf2Data->get_debug_info_data(), &offset); + if (!success) + s->Printf("ERROR\n"); + // else + // valid_cu_offsets.push_back(cu.GetOffset()); + + cu.Verify(verifyInfo.strm); + offset = cu.GetNextCompileUnitOffset(); + } + + if (success) + s->Printf("OK\n"); + + s->Printf("Verifying address ranges and siblings..."); + if (verbose) + s->EOL(); + DWARFDebugInfo::Parse(dwarf2Data, VerifyCallback, &verifyInfo); + +// VerifyAddressRangesForCU(&verifyInfo); + + if (verifyInfo.addr_range_errors > 0) + s->Printf("\nERRORS - %u error(s) were found.\n", verifyInfo.addr_range_errors); + else + s->Printf("OK\n"); + + uint32_t total_category_sizes[kNumTagCategories] = {0}; + uint32_t total_category_count[kNumTagCategories] = {0}; + uint32_t total_die_count = 0; + uint32_t total_die_size = 0; + + typedef set DIEStatBySizeMap; + + s->PutCString( "\n" + "DWARF Statistics\n" + "Count Size Size % Tag\n" + "-------- -------- -------- -------------------------------------------\n"); + DIEStatBySizeMap statBySizeMap; + DIEStatMap::const_iterator pos; + DIEStatMap::const_iterator end_pos = verifyInfo.die_stats.end(); + for (pos = verifyInfo.die_stats.begin(); pos != end_pos; ++pos) + { + const uint32_t die_count = pos->second.count; + const uint32_t die_size = pos->second.byte_size; + + statBySizeMap.insert(pos); + total_die_count += die_count; + total_die_size += die_size; + DW_TAG_CategoryEnum category = get_tag_category(pos->first); + total_category_sizes[category] += die_size; + total_category_count[category] += die_count; + } + + float total_die_size_float = total_die_size; + + DIEStatBySizeMap::const_reverse_iterator size_pos; + DIEStatBySizeMap::const_reverse_iterator size_pos_end = statBySizeMap.rend(); + float percentage; + for (size_pos = statBySizeMap.rbegin(); size_pos != size_pos_end; ++size_pos) + { + pos = *size_pos; + + const DIEStat& tag_stat = pos->second; + + const uint32_t die_count = tag_stat.count; + const uint32_t die_size = tag_stat.byte_size; + percentage = ((float)die_size/total_die_size_float)*100.0; + s->Printf("%7u %8u %2.2f%% %s\n", die_count, die_size, percentage, DW_TAG_value_to_name(pos->first)); + + const DwarfAttrStatMap& attr_stats = tag_stat.attr_stats; + if (!attr_stats.empty()) + { + typedef set DwarfAttrStatBySizeMap; + DwarfAttrStatBySizeMap attrStatBySizeMap; + DwarfAttrStatMap::const_iterator attr_stat_pos; + DwarfAttrStatMap::const_iterator attr_stat_pos_end = attr_stats.end(); + for (attr_stat_pos = attr_stats.begin(); attr_stat_pos != attr_stat_pos_end; ++attr_stat_pos) + { + attrStatBySizeMap.insert(attr_stat_pos); + } + + DwarfAttrStatBySizeMap::const_reverse_iterator attr_size_pos; + DwarfAttrStatBySizeMap::const_reverse_iterator attr_size_pos_end = attrStatBySizeMap.rend(); + for (attr_size_pos = attrStatBySizeMap.rbegin(); attr_size_pos != attr_size_pos_end; ++attr_size_pos) + { + attr_stat_pos = *attr_size_pos; + percentage = ((float)attr_stat_pos->second.byte_size/die_size)*100.0; + s->Printf("%7u %8u %2.2f%% %s\n", attr_stat_pos->second.count, attr_stat_pos->second.byte_size, percentage, DW_AT_value_to_name(attr_stat_pos->first)); + } + s->EOL(); + } + } + + s->Printf("-------- -------- -------- -------------------------------------------\n"); + s->Printf("%7u %8u 100.00% Total for all DIEs\n", total_die_count, total_die_size); + + float total_category_percentages[kNumTagCategories] = + { + ((float)total_category_sizes[TagCategoryVariable]/total_die_size_float)*100.0, + ((float)total_category_sizes[TagCategoryType]/total_die_size_float)*100.0, + ((float)total_category_sizes[TagCategoryProgram]/total_die_size_float)*100.0 + }; + + s->EOL(); + s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryVariable], total_category_sizes[TagCategoryVariable], total_category_percentages[TagCategoryVariable], "Total for variable related DIEs"); + s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryType], total_category_sizes[TagCategoryType], total_category_percentages[TagCategoryType], "Total for type related DIEs"); + s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryProgram], total_category_sizes[TagCategoryProgram], total_category_percentages[TagCategoryProgram], "Total for program related DIEs"); + s->Printf("\n\n"); +} + +typedef struct DumpInfo +{ + DumpInfo(Stream* init_strm, uint32_t off, uint32_t depth) : + strm(init_strm), + die_offset(off), + recurse_depth(depth), + found_depth(UINT_MAX), + found_die(false), + ancestors() + { + } + Stream* strm; + const uint32_t die_offset; + const uint32_t recurse_depth; + uint32_t found_depth; + bool found_die; + std::vector ancestors; + + DISALLOW_COPY_AND_ASSIGN(DumpInfo); +} DumpInfo; + +//---------------------------------------------------------------------- +// DumpCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function dump DWARF information and obey recurse depth and +// wether a single DIE is to be dumped (or all of the data). +//---------------------------------------------------------------------- +static dw_offset_t DumpCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + DumpInfo* dumpInfo = (DumpInfo*)userData; + + const DWARFCompileUnit* cu = cu_sp.get(); + + Stream *s = dumpInfo->strm; + bool show_parents = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_ShowAncestors); + + if (die) + { + // Are we dumping everything? + if (dumpInfo->die_offset == DW_INVALID_OFFSET) + { + // Yes we are dumping everything. Obey our recurse level though + if (curr_depth < dumpInfo->recurse_depth) + die->Dump(dwarf2Data, cu, s, 0); + } + else + { + // We are dumping a specific DIE entry by offset + if (dumpInfo->die_offset == die->GetOffset()) + { + // We found the DIE we were looking for, dump it! + if (show_parents) + { + s->SetIndentLevel(0); + const uint32_t num_ancestors = dumpInfo->ancestors.size(); + if (num_ancestors > 0) + { + for (uint32_t i=0; iancestors[i].Dump(dwarf2Data, cu, s, 0); + s->IndentMore(); + } + } + } + + dumpInfo->found_depth = curr_depth; + + die->Dump(dwarf2Data, cu, s, 0); + + // Note that we found the DIE we were looking for + dumpInfo->found_die = true; + + // Since we are dumping a single DIE, if there are no children we are done! + if (!die->HasChildren() || dumpInfo->recurse_depth == 0) + return DW_INVALID_OFFSET; // Return an invalid address to end parsing + } + else if (dumpInfo->found_die) + { + // Are we done with all the children? + if (curr_depth <= dumpInfo->found_depth) + return DW_INVALID_OFFSET; + + // We have already found our DIE and are printing it's children. Obey + // our recurse depth and return an invalid offset if we get done + // dumping all the the children + if (dumpInfo->recurse_depth == UINT_MAX || curr_depth <= dumpInfo->found_depth + dumpInfo->recurse_depth) + die->Dump(dwarf2Data, cu, s, 0); + } + else if (dumpInfo->die_offset > die->GetOffset()) + { + if (show_parents) + dumpInfo->ancestors.back() = *die; + } + } + + // Keep up with our indent level + if (die->IsNULL()) + { + if (show_parents) + dumpInfo->ancestors.pop_back(); + + if (curr_depth <= 1) + return cu->GetNextCompileUnitOffset(); + else + s->IndentLess(); + } + else if (die->HasChildren()) + { + if (show_parents) + { + DWARFDebugInfoEntry null_die; + dumpInfo->ancestors.push_back(null_die); + } + s->IndentMore(); + } + } + else + { + if (cu == NULL) + s->PutCString("NULL - cu"); + // We have a compile unit, reset our indent level to zero just in case + s->SetIndentLevel(0); + + // See if we are dumping everything? + if (dumpInfo->die_offset == DW_INVALID_OFFSET) + { + // We are dumping everything + cu->Dump(s); + return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit + } + else + { + if (show_parents) + { + dumpInfo->ancestors.clear(); + dumpInfo->ancestors.resize(1); + } + + // We are dumping only a single DIE possibly with it's children and + // we must find it's compile unit before we can dump it properly + if (dumpInfo->die_offset < cu->GetFirstDIEOffset()) + { + // Not found, maybe the DIE offset provided wasn't correct? + // *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " was not found." << endl; + return DW_INVALID_OFFSET; + } + else + { + // See if the DIE is in this compile unit? + if (dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) + { + // This DIE is in this compile unit! + if (s->GetVerbose()) + cu->Dump(s); // Dump the compile unit for the DIE in verbose mode + + return next_offset; + // // We found our compile unit that contains our DIE, just skip to dumping the requested DIE... + // return dumpInfo->die_offset; + } + else + { + // Skip to the next compile unit as the DIE isn't in the current one! + return cu->GetNextCompileUnitOffset(); + } + } + } + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// Dump +// +// Dump the information in the .debug_info section to the specified +// ostream. If die_offset is valid, a single DIE will be dumped. If the +// die_offset is invalid, all the DWARF information will be dumped. Both +// cases will obey a "recurse_depth" or how deep to traverse into the +// children of each DIE entry. A recurse_depth of zero will dump all +// compile unit headers. A recurse_depth of 1 will dump all compile unit +// headers and the DW_TAG_compile unit tags. A depth of 2 will also +// dump all types and functions. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Dump +( + Stream *s, + SymbolFileDWARF* dwarf2Data, + const uint32_t die_offset, + const uint32_t recurse_depth +) +{ + DumpInfo dumpInfo(s, die_offset, recurse_depth); + s->PutCString(".debug_info contents"); + if (dwarf2Data->get_debug_info_data().GetByteSize() > 0) + { + if (die_offset == DW_INVALID_OFFSET) + s->PutCString(":\n"); + else + { + s->Printf(" for DIE entry at .debug_info[0x%8.8x]", die_offset); + if (recurse_depth != UINT_MAX) + s->Printf(" recursing %u levels deep.", recurse_depth); + s->EOL(); + } + } + else + { + s->PutCString(": < EMPTY >\n"); + return; + } + DWARFDebugInfo::Parse(dwarf2Data, DumpCallback, &dumpInfo); +} + + +//---------------------------------------------------------------------- +// Dump +// +// Dump the contents of this DWARFDebugInfo object as has been parsed +// and/or modified after it has been parsed. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Dump (Stream *s, const uint32_t die_offset, const uint32_t recurse_depth) +{ + DumpInfo dumpInfo(s, die_offset, recurse_depth); + + s->PutCString("Dumping .debug_info section from internal representation\n"); + + CompileUnitColl::const_iterator pos; + uint32_t curr_depth = 0; + ParseCompileUnitHeadersIfNeeded(); + for (pos = m_compile_units.begin(); pos != m_compile_units.end(); ++pos) + { + const DWARFCompileUnitSP& cu_sp = *pos; + DumpCallback(m_dwarf2Data, (DWARFCompileUnitSP&)cu_sp, NULL, 0, curr_depth, &dumpInfo); + cu_sp->DIE()->Dump(m_dwarf2Data, cu_sp.get(), s, recurse_depth); + } +} + + +//---------------------------------------------------------------------- +// FindCallbackString +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function will find the die_offset of any items whose DW_AT_name +// matches the given string +//---------------------------------------------------------------------- +typedef struct FindCallbackStringInfoTag +{ + const char* name; + bool ignore_case; + RegularExpression* regex; + vector& die_offsets; +} FindCallbackStringInfo; + +static dw_offset_t FindCallbackString +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + FindCallbackStringInfo* info = (FindCallbackStringInfo*)userData; + const DWARFCompileUnit* cu = cu_sp.get(); + + if (die) + { + const char* die_name = die->GetName(dwarf2Data, cu); + if (die_name) + { + if (info->regex) + { + if (info->regex->Execute(die_name)) + info->die_offsets.push_back(die->GetOffset()); + } + else + { + if ((info->ignore_case ? strcasecmp(die_name, info->name) : strcmp(die_name, info->name)) == 0) + info->die_offsets.push_back(die->GetOffset()); + } + } + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// Find +// +// Finds all DIE that have a specific DW_AT_name attribute by manually +// searching through the debug information (not using the +// .debug_pubnames section). The string must match the entire name +// and case sensitive searches are an option. +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::Find(const char* name, bool ignore_case, vector& die_offsets) const +{ + die_offsets.clear(); + if (name && name[0]) + { + FindCallbackStringInfo info = { name, ignore_case, NULL, die_offsets }; + DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); + } + return !die_offsets.empty(); +} + +//---------------------------------------------------------------------- +// Find +// +// Finds all DIE that have a specific DW_AT_name attribute by manually +// searching through the debug information (not using the +// .debug_pubnames section). The string must match the supplied regular +// expression. +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::Find(RegularExpression& re, vector& die_offsets) const +{ + die_offsets.clear(); + FindCallbackStringInfo info = { NULL, false, &re, die_offsets }; + DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); + return !die_offsets.empty(); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h new file mode 100644 index 000000000000..f506a3de6e0a --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -0,0 +1,86 @@ +//===-- DWARFDebugInfo.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugInfo_h_ +#define SymbolFileDWARF_DWARFDebugInfo_h_ + +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/lldb-private.h" +#include "SymbolFileDWARF.h" + +typedef std::multimap CStringToDIEMap; +typedef CStringToDIEMap::iterator CStringToDIEMapIter; +typedef CStringToDIEMap::const_iterator CStringToDIEMapConstIter; + +typedef lldb::SharedPtr::Type DWARFCompileUnitSP; + +class DWARFDebugInfo +{ +public: + typedef dw_offset_t (*Callback)( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_shared_ptr, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t depth, + void* userData); + + DWARFDebugInfo(); + void SetDwarfData(SymbolFileDWARF* dwarf2Data); + bool BuildFunctionAddressRangeTable(DWARFDebugAranges* debug_aranges); + + bool LookupAddress( + const dw_addr_t address, + const dw_offset_t cu_offset, // Can be valid (find in .debug_aranges), or DW_INVALID_OFFSET if we need to search manually + DWARFCompileUnitSP& cu_shared_ptr, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + void AddCompileUnit(DWARFCompileUnitSP& cu); + uint32_t GetNumCompileUnits(); + DWARFCompileUnit* GetCompileUnitAtIndex(uint32_t idx); + DWARFCompileUnitSP GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr = NULL); + DWARFCompileUnitSP GetCompileUnitContainingDIE(dw_offset_t die_offset); + + DWARFDebugInfoEntry* GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr); + const DWARFDebugInfoEntry* GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr); + + void Dump(lldb_private::Stream *s, const uint32_t die_offset, const uint32_t recurse_depth); + static void Parse(SymbolFileDWARF* parser, Callback callback, void* userData); + static void Verify(lldb_private::Stream *s, SymbolFileDWARF* dwarf2Data); + static void Dump(lldb_private::Stream *s, SymbolFileDWARF* dwarf2Data, const uint32_t die_offset, const uint32_t recurse_depth); + bool Find(const char* name, bool ignore_case, std::vector& die_offsets) const; + bool Find(lldb_private::RegularExpression& re, std::vector& die_offsets) const; + + enum + { + eDumpFlag_Verbose = (1<<0), // Verbose dumping + eDumpFlag_ShowForm = (1<<1), // Show the DW_form type + eDumpFlag_EnglishyNames = (1<<2), // Show the DW_TAG, DW_AT and DW_FORM types in more englishy names instead of as DWARF definitions values + eDumpFlag_ShowAncestors = (1<<3) // Show all parent DIEs when dumping single DIEs + }; + + +protected: + SymbolFileDWARF* m_dwarf2Data; + typedef std::vector CompileUnitColl; + + CompileUnitColl m_compile_units; + +private: + // All parsing needs to be done partially any managed by this class as accessors are called. + void ParseCompileUnitHeadersIfNeeded(); + + DISALLOW_COPY_AND_ASSIGN (DWARFDebugInfo); +}; + +#endif // SymbolFileDWARF_DWARFDebugInfo_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 000000000000..19eef06d3d16 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,1929 @@ +//===-- DWARFDebugInfoEntry.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugInfoEntry.h" + +#include + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "DWARFCompileUnit.h" +#include "SymbolFileDWARF.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFLocationDescription.h" +#include "DWARFLocationList.h" +#include "DWARFDebugRanges.h" + +using namespace lldb_private; +using namespace std; +extern int g_verbose; + + + +DWARFDebugInfoEntry::Attributes::Attributes() : + m_infos() +{ + m_infos.reserve(20); +} + +DWARFDebugInfoEntry::Attributes::~Attributes() +{ +} + + +uint32_t +DWARFDebugInfoEntry::Attributes::FindAttributeIndex(dw_attr_t attr) const +{ + std::vector::const_iterator end = m_infos.end(); + std::vector::const_iterator beg = m_infos.begin(); + std::vector::const_iterator pos; + for (pos = beg; pos != end; ++pos) + { + if (pos->attr == attr) + return std::distance(beg, pos); + } + return UINT_MAX; +} + +void +DWARFDebugInfoEntry::Attributes::Append(const DWARFCompileUnit *cu, dw_offset_t attr_die_offset, dw_attr_t attr, dw_form_t form) +{ + Info info = { cu, attr_die_offset, attr, form }; + m_infos.push_back(info); +} + +bool +DWARFDebugInfoEntry::Attributes::ContainsAttribute(dw_attr_t attr) const +{ + return FindAttributeIndex(attr) != UINT_MAX; +} + +bool +DWARFDebugInfoEntry::Attributes::RemoveAttribute(dw_attr_t attr) +{ + uint32_t attr_index = FindAttributeIndex(attr); + if (attr_index != UINT_MAX) + { + m_infos.erase(m_infos.begin() + attr_index); + return true; + } + return false; +} + +bool +DWARFDebugInfoEntry::Attributes::ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const +{ + form_value.SetForm(FormAtIndex(i)); + dw_offset_t offset = DIEOffsetAtIndex(i); + return form_value.ExtractValue(dwarf2Data->get_debug_info_data(), &offset, CompileUnitAtIndex(i)); +} + +uint64_t +DWARFDebugInfoEntry::Attributes::FormValueAsUnsignedAtIndex(SymbolFileDWARF* dwarf2Data, uint32_t i, uint64_t fail_value) const +{ + DWARFFormValue form_value; + if (ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + return form_value.Reference(CompileUnitAtIndex(i)); + return fail_value; +} + + +//---------------------------------------------------------------------- +// Extract +// +// Extract a debug info entry for a given compile unit from the +// .debug_info and .debug_abbrev data within the SymbolFileDWARF class +// starting at the given offset +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::Extract +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + uint32_t* offset_ptr +) +{ + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); +// const DataExtractor& debug_str_data = dwarf2Data->get_debug_str_data(); + const uint32_t cu_end_offset = cu->GetNextCompileUnitOffset(); + const uint8_t cu_addr_size = cu->GetAddressByteSize(); + uint32_t offset = *offset_ptr; +// if (offset >= cu_end_offset) +// Log::Error("DIE at offset 0x%8.8x is beyond the end of the current compile unit (0x%8.8x)", m_offset, cu_end_offset); + if ((offset < cu_end_offset) && debug_info_data.ValidOffset(offset)) + { + m_offset = offset; + + dw_uleb128_t abbrCode = debug_info_data.GetULEB128(&offset); + + if (abbrCode) + { + m_abbrevDecl = cu->GetAbbreviations()->GetAbbreviationDeclaration(abbrCode); + + if (m_abbrevDecl) + { + dw_tag_t tag = m_abbrevDecl->Tag(); + + bool isCompileUnitTag = tag == DW_TAG_compile_unit; + if (cu && isCompileUnitTag) + ((DWARFCompileUnit*)cu)->SetBaseAddress(0); + + // Skip all data in the .debug_info for the attributes + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; iGetAttrAndFormByIndexUnchecked(i, attr, form); + + if (isCompileUnitTag && ((attr == DW_AT_entry_pc) || (attr == DW_AT_low_pc))) + { + DWARFFormValue form_value(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + if (attr == DW_AT_low_pc || attr == DW_AT_entry_pc) + ((DWARFCompileUnit*)cu)->SetBaseAddress(form_value.Unsigned()); + } + } + else + { +die_extract_indirect_form: + register uint32_t form_size = 0; + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_block : form_size = debug_info_data.GetULEB128(&offset); break; + case DW_FORM_block1 : form_size = debug_info_data.GetU8(&offset); break; + case DW_FORM_block2 : form_size = debug_info_data.GetU16(&offset); break; + case DW_FORM_block4 : form_size = debug_info_data.GetU32(&offset); break; + + // Inlined NULL terminated C-strings + case DW_FORM_string : + { +// const char *s = + debug_info_data.GetCStr(&offset); +// switch (attr) +// { +// case DW_AT_name: m_name = s; break; +// case DW_AT_MIPS_linkage_name: m_linkage_name = s; break; +// default: break; +// } + } + break; + + // Compile unit address sized values + case DW_FORM_addr : + case DW_FORM_ref_addr : + form_size = cu_addr_size; + break; + + // 1 byte values + case DW_FORM_data1 : + case DW_FORM_flag : + case DW_FORM_ref1 : + form_size = 1; + break; + + // 2 byte values + case DW_FORM_data2 : + case DW_FORM_ref2 : + form_size = 2; + break; + + // 4 byte values + case DW_FORM_strp : +// switch (attr) +// { +// case DW_AT_name: +// m_name = debug_str_data.PeekCStr(debug_info_data.GetU32(&offset)); +// break; +// case DW_AT_MIPS_linkage_name: +// m_linkage_name = debug_str_data.PeekCStr(debug_info_data.GetU32(&offset)); +// break; +// +// default: + form_size = 4; +// break; +// } + break; + + case DW_FORM_data4 : + case DW_FORM_ref4 : + form_size = 4; + break; + + // 8 byte values + case DW_FORM_data8 : + case DW_FORM_ref8 : + form_size = 8; + break; + + // signed or unsigned LEB 128 values + // case DW_FORM_APPLE_db_str: + case DW_FORM_sdata : + case DW_FORM_udata : + case DW_FORM_ref_udata : + debug_info_data.Skip_LEB128(&offset); + break; + + case DW_FORM_indirect : + form = debug_info_data.GetULEB128(&offset); + goto die_extract_indirect_form; + + default: + *offset_ptr = offset; + return false; + } + + offset += form_size; + } + } + *offset_ptr = offset; + return true; + } + } + else + { + m_abbrevDecl = NULL; + *offset_ptr = offset; + return true; // NULL debug tag entry + } + } + + return false; +} + +//---------------------------------------------------------------------- +// AppendDependentDIES() +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::AppendDependentDIES +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const bool add_children, + DWARFDIECollection& dependent_dies +) const +{ + // Add this object's DIE offset + // The line below is the only place that should add a die to the + // dependent_dies collection as we have to be careful of recursion! + if ( !dependent_dies.Insert(this) ) + return false; // This DIE already exists in the collection, nothing to do! + + //DEBUG_PRINTF(" dependent_dies.Insert(0x%8.8x)\n", GetOffset());/// + + if (m_abbrevDecl) + { + // Keep adding parent DIE offsets as long as the offsets do not + // already exist in the collection + const DWARFDebugInfoEntry* die = GetParent(); + while ( die && die->AppendDependentDIES(dwarf2Data, cu, false, dependent_dies) ) + die = die->GetParent(); + + bool add_non_subprogram_children = false; + bool add_children_override = false; + + if (!add_children) + { + switch (m_abbrevDecl->Tag()) + { + case DW_TAG_array_type: break; + case DW_TAG_class_type: add_non_subprogram_children = true; break; + case DW_TAG_entry_point: break; + case DW_TAG_enumeration_type: break; + case DW_TAG_formal_parameter: break; + case DW_TAG_imported_declaration: break; + case DW_TAG_label: break; + case DW_TAG_lexical_block: add_children_override = true; break; + case DW_TAG_member: break; + case DW_TAG_pointer_type: break; + case DW_TAG_reference_type: break; + case DW_TAG_compile_unit: break; + case DW_TAG_string_type: break; + case DW_TAG_structure_type: add_non_subprogram_children = true; break; + case DW_TAG_subroutine_type: add_children_override = true; break; + case DW_TAG_typedef: break; + case DW_TAG_union_type: add_non_subprogram_children = true; break; + case DW_TAG_unspecified_parameters: break; + case DW_TAG_variant: break; + case DW_TAG_common_block: break; + case DW_TAG_common_inclusion: break; + case DW_TAG_inheritance: break; + case DW_TAG_inlined_subroutine: break; + case DW_TAG_module: break; + case DW_TAG_ptr_to_member_type: break; + case DW_TAG_set_type: break; + case DW_TAG_subrange_type: break; + case DW_TAG_with_stmt: break; + case DW_TAG_access_declaration: break; + case DW_TAG_base_type: break; + case DW_TAG_catch_block: break; + case DW_TAG_const_type: break; + case DW_TAG_constant: break; + case DW_TAG_enumerator: break; + case DW_TAG_file_type: break; + case DW_TAG_friend: break; + case DW_TAG_namelist: break; + case DW_TAG_namelist_item: break; + case DW_TAG_packed_type: break; + case DW_TAG_subprogram: add_children_override = true; break; + case DW_TAG_template_type_parameter: break; + case DW_TAG_template_value_parameter: break; + case DW_TAG_thrown_type: break; + case DW_TAG_try_block: break; + case DW_TAG_variant_part: break; + case DW_TAG_variable: break; + case DW_TAG_volatile_type: break; + case DW_TAG_dwarf_procedure: break; + case DW_TAG_restrict_type: break; + case DW_TAG_interface_type: break; + case DW_TAG_namespace: break; + case DW_TAG_imported_module: break; + case DW_TAG_unspecified_type: break; + case DW_TAG_partial_unit: break; + case DW_TAG_imported_unit: break; + case DW_TAG_shared_type: break; + } + } + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + // Dump all data in the .debug_info for the attributes + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_offset_t offset = GetOffset(); + debug_info_data.Skip_LEB128(&offset); // Skip abbreviation code + + dw_attr_t attr; + dw_form_t form; + for (i=0; iGetAttrAndFormByIndexUnchecked(i, attr, form); + DWARFFormValue form_value(form); + + switch (attr) + { + // All cases that use refer to another DIE should use this case + // without + // having to check the FORM of the attribute to tell if it refers to another + // DIE + case DW_AT_abstract_origin: + case DW_AT_import: + case DW_AT_discr: + case DW_AT_containing_type: + case DW_AT_base_types: + case DW_AT_friend: + case DW_AT_specification: + case DW_AT_type: + case DW_AT_common_reference: + case DW_AT_default_value: + { + form_value.ExtractValue(debug_info_data, &offset, cu); + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* ref_die = const_cast(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + if (ref_die) + ref_die->AppendDependentDIES(dwarf2Data, cu_sp_ptr.get(), true, dependent_dies); + } + break; + + default: + if (attr != DW_AT_sibling) + { + switch (form_value.Form()) + { + case DW_FORM_ref_addr: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: +// Log::WarningVerbose("DWARFDebugInfoEntry::AppendDependentDIES() -- check on this item %s: attr = %s form = %s", +// DW_TAG_value_to_name(m_abbrevDecl->Tag()), +// DW_AT_value_to_name(attr), +// DW_FORM_value_to_name(form)); + break; + } + } + form_value.SkipValue(debug_info_data, &offset, cu); + break; + } + } + + if (m_abbrevDecl->HasChildren()) + { + const DWARFDebugInfoEntry* child; + for (child = GetFirstChild(); child != NULL; child = child->GetSibling()) + { + bool add = add_children || add_children_override; + + if (!add) + { + if (add_non_subprogram_children) + { + // add_non_subprogram_children is used for classes and structs + // that may contain children that are the member variables that + // may have functions as children and whom may add the class or + // struct by adding their parent. We don't want to add any + // functions though since they may have been optimized out. But + // we do need to watch for declarations and keep them. + if (child->Tag() == DW_TAG_subprogram) + { + // Check if this subprogram TAG had a DW_AT_declaration attribute set to 1. + // If so we need to include this DIE so that we always have a complete view + // of a class definition so debuggers can track down any weak symbols that + // may not have had weak definition entries. + if (child->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_declaration, 0) == 1) + add = true; + } + else + { + // Add all other items inside a class/struct + add = true; + } + } + else + { + // We don't need to add this child, only add it if it's a NULL tag + add = child->IsNULL(); + } + } + + if (add) + child->AppendDependentDIES(dwarf2Data, cu, true, dependent_dies); + } + } + } + return true; +} + +//---------------------------------------------------------------------- +// DumpAncestry +// +// Dumps all of a debug information entries parents up until oldest and +// all of it's attributes to the specified stream. +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::DumpAncestry +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* oldest, + Stream *s, + uint32_t recurse_depth +) const +{ + const DWARFDebugInfoEntry* parent = GetParent(); + if (parent && parent != oldest) + parent->DumpAncestry(dwarf2Data, cu, oldest, s, 0); + Dump(dwarf2Data, cu, s, recurse_depth); +} + +//---------------------------------------------------------------------- +// Compare two DIE by comparing all their attributes values, and +// following all DW_FORM_ref attributes and comparing their contents as +// well (except for DW_AT_sibling attributes. +// +// DWARFDebugInfoEntry::CompareState compare_state; +// int result = DWARFDebugInfoEntry::Compare(this, 0x00017ccb, 0x0001eb2b, compare_state, false, true); +//---------------------------------------------------------------------- +int +DWARFDebugInfoEntry::Compare +( + SymbolFileDWARF* dwarf2Data, + dw_offset_t a_die_offset, + dw_offset_t b_die_offset, + CompareState &compare_state, + bool compare_siblings, + bool compare_children +) +{ + if (a_die_offset == b_die_offset) + return 0; + + DWARFCompileUnitSP a_cu_sp; + DWARFCompileUnitSP b_cu_sp; + const DWARFDebugInfoEntry* a_die = dwarf2Data->DebugInfo()->GetDIEPtr(a_die_offset, &a_cu_sp); + const DWARFDebugInfoEntry* b_die = dwarf2Data->DebugInfo()->GetDIEPtr(b_die_offset, &b_cu_sp); + + return Compare(dwarf2Data, a_cu_sp.get(), a_die, b_cu_sp.get(), b_die, compare_state, compare_siblings, compare_children); +} + +int +DWARFDebugInfoEntry::Compare +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* a_cu, const DWARFDebugInfoEntry* a_die, + DWARFCompileUnit* b_cu, const DWARFDebugInfoEntry* b_die, + CompareState &compare_state, + bool compare_siblings, + bool compare_children +) +{ + if (a_die == b_die) + return 0; + + if (!compare_state.AddTypePair(a_die->GetOffset(), b_die->GetOffset())) + { + // We are already comparing both of these types, so let + // compares complete for the real result + return 0; + } + + //printf("DWARFDebugInfoEntry::Compare(0x%8.8x, 0x%8.8x)\n", a_die->GetOffset(), b_die->GetOffset()); + + // Do we have two valid DIEs? + if (a_die && b_die) + { + // Both DIE are valid + int result = 0; + + const dw_tag_t a_tag = a_die->Tag(); + const dw_tag_t b_tag = b_die->Tag(); + if (a_tag == 0 && b_tag == 0) + return 0; + + //printf(" comparing tags: %s and %s\n", DW_TAG_value_to_name(a_tag), DW_TAG_value_to_name(b_tag)); + + if (a_tag < b_tag) + return -1; + else if (a_tag > b_tag) + return 1; + + DWARFDebugInfoEntry::Attributes a_attrs; + DWARFDebugInfoEntry::Attributes b_attrs; + size_t a_attr_count = a_die->GetAttributes(dwarf2Data, a_cu, a_attrs); + size_t b_attr_count = b_die->GetAttributes(dwarf2Data, b_cu, b_attrs); + if (a_attr_count != b_attr_count) + { + a_attrs.RemoveAttribute(DW_AT_sibling); + b_attrs.RemoveAttribute(DW_AT_sibling); + } + + a_attr_count = a_attrs.Size(); + b_attr_count = b_attrs.Size(); + + DWARFFormValue a_form_value; + DWARFFormValue b_form_value; + + if (a_attr_count != b_attr_count) + { + uint32_t is_decl_index = a_attrs.FindAttributeIndex(DW_AT_declaration); + uint32_t a_name_index = UINT_MAX; + uint32_t b_name_index = UINT_MAX; + if (is_decl_index != UINT_MAX) + { + if (a_attr_count == 2) + { + a_name_index = a_attrs.FindAttributeIndex(DW_AT_name); + b_name_index = b_attrs.FindAttributeIndex(DW_AT_name); + } + } + else + { + is_decl_index = b_attrs.FindAttributeIndex(DW_AT_declaration); + if (is_decl_index != UINT_MAX && a_attr_count == 2) + { + a_name_index = a_attrs.FindAttributeIndex(DW_AT_name); + b_name_index = b_attrs.FindAttributeIndex(DW_AT_name); + } + } + if (a_name_index != UINT_MAX && b_name_index != UINT_MAX) + { + if (a_attrs.ExtractFormValueAtIndex(dwarf2Data, a_name_index, a_form_value) && + b_attrs.ExtractFormValueAtIndex(dwarf2Data, b_name_index, b_form_value)) + { + result = DWARFFormValue::Compare (a_form_value, b_form_value, a_cu, b_cu, &dwarf2Data->get_debug_str_data()); + if (result == 0) + { + a_attr_count = b_attr_count = 0; + compare_children = false; + } + } + } + } + + if (a_attr_count < b_attr_count) + return -1; + if (a_attr_count > b_attr_count) + return 1; + + + // The number of attributes are the same... + if (a_attr_count > 0) + { + const DataExtractor* debug_str_data_ptr = &dwarf2Data->get_debug_str_data(); + + uint32_t i; + for (i=0; i b_attr) + return 1; + + switch (a_attr) + { + // Since we call a form of GetAttributes which inlines the + // attributes from DW_AT_abstract_origin and DW_AT_specification + // we don't care if their values mismatch... + case DW_AT_abstract_origin: + case DW_AT_specification: + case DW_AT_sibling: + case DW_AT_containing_type: + //printf(" action = IGNORE\n"); + result = 0; + break; // ignore + + default: + if (a_attrs.ExtractFormValueAtIndex(dwarf2Data, i, a_form_value) && + b_attrs.ExtractFormValueAtIndex(dwarf2Data, i, b_form_value)) + result = DWARFFormValue::Compare (a_form_value, b_form_value, a_cu, b_cu, debug_str_data_ptr); + break; + } + + //printf("\t result = %i\n", result); + + if (result != 0) + { + // Attributes weren't equal, lets see if we care? + switch (a_attr) + { + case DW_AT_decl_file: + // TODO: add the ability to compare files in two different compile units + if (a_cu == b_cu) + { + //printf(" action = RETURN RESULT\n"); + return result; // Only return the compare results when the compile units are the same and the decl_file attributes can be compared + } + else + { + result = 0; + //printf(" action = IGNORE\n"); + } + break; + + default: + switch (a_attrs.FormAtIndex(i)) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_ref_addr: + //printf(" action = COMPARE DIEs 0x%8.8x 0x%8.8x\n", (dw_offset_t)a_form_value.Reference(a_cu), (dw_offset_t)b_form_value.Reference(b_cu)); + // These attribute values refer to other DIEs, so lets compare those instead of their DIE offsets... + result = Compare(dwarf2Data, a_form_value.Reference(a_cu), b_form_value.Reference(b_cu), compare_state, false, true); + if (result != 0) + return result; + break; + + default: + // We do care that they were different, return this result... + //printf(" action = RETURN RESULT\n"); + return result; + } + } + } + } + } + //printf(" SUCCESS\n\t\t0x%8.8x: %s\n\t\t0x%8.8x: %s\n", a_die->GetOffset(), DW_TAG_value_to_name(a_tag), b_die->GetOffset(), DW_TAG_value_to_name(b_tag)); + + if (compare_children) + { + bool a_has_children = a_die->HasChildren(); + bool b_has_children = b_die->HasChildren(); + if (a_has_children == b_has_children) + { + // Both either have kids or don't + if (a_has_children) + result = Compare( dwarf2Data, + a_cu, a_die->GetFirstChild(), + b_cu, b_die->GetFirstChild(), + compare_state, true, compare_children); + else + result = 0; + } + else if (!a_has_children) + result = -1; // A doesn't have kids, but B does + else + result = 1; // A has kids, but B doesn't + } + + if (compare_siblings) + { + result = Compare( dwarf2Data, + a_cu, a_die->GetSibling(), + b_cu, b_die->GetSibling(), + compare_state, true, compare_children); + } + + return result; + } + + if (a_die == NULL) + return -1; // a_die is NULL, yet b_die is non-NULL + else + return 1; // a_die is non-NULL, yet b_die is NULL + +} + +// +//int +//DWARFDebugInfoEntry::Compare +//( +// SymbolFileDWARF* dwarf2Data, +// const DWARFCompileUnit* cu_a, +// const DWARFDebugInfoEntry* die_a, +// const DWARFCompileUnit* cu_a, +// const DWARFDebugInfoEntry* die_b, +// CompareState &compare_state +//) +//{ +//} + +//---------------------------------------------------------------------- +// GetDIENamesAndRanges +// +// Gets the valid address ranges for a given DIE by looking for a +// DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges +// attributes. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetDIENamesAndRanges +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const char * &name, + const char * &mangled, + DWARFDebugRanges::RangeList& ranges, + int& decl_file, + int& decl_line, + int& decl_column, + int& call_file, + int& call_line, + int& call_column, + DWARFExpression *frame_base +) const +{ + if (dwarf2Data == NULL) + return false; + + dw_addr_t lo_pc = DW_INVALID_ADDRESS; + dw_addr_t hi_pc = DW_INVALID_ADDRESS; + std::vector die_offsets; + if (m_abbrevDecl) + { + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + uint32_t offset = m_offset; + + if (!debug_info_data.ValidOffset(offset)) + return false; + + // Skip the abbreviation code + debug_info_data.Skip_LEB128(&offset); + + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; iGetAttrAndFormByIndexUnchecked(i, attr, form); + DWARFFormValue form_value(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + switch (attr) + { + case DW_AT_low_pc: + case DW_AT_entry_pc: + lo_pc = form_value.Unsigned(); + break; + + case DW_AT_high_pc: + hi_pc = form_value.Unsigned(); + break; + + case DW_AT_ranges: + { + const DWARFDebugRanges* debug_ranges = dwarf2Data->DebugRanges(); + debug_ranges->FindRanges(form_value.Unsigned(), ranges); + // All DW_AT_ranges are relative to the base address of the + // compile unit. We add the compile unit base address to make + // sure all the addresses are properly fixed up. + ranges.AddOffset(cu->GetBaseAddress()); + } + break; + + case DW_AT_name: + if (name == NULL) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + break; + + case DW_AT_MIPS_linkage_name: + if (mangled == NULL) + mangled = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + break; + + case DW_AT_abstract_origin: + die_offsets.push_back(form_value.Reference(cu)); + break; + + case DW_AT_specification: + die_offsets.push_back(form_value.Reference(cu)); + break; + + case DW_AT_decl_file: + if (decl_file == 0) + decl_file = form_value.Unsigned(); + break; + + case DW_AT_decl_line: + if (decl_line == 0) + decl_line = form_value.Unsigned(); + break; + + case DW_AT_decl_column: + if (decl_column == 0) + decl_column = form_value.Unsigned(); + break; + + case DW_AT_call_file: + if (call_file == 0) + call_file = form_value.Unsigned(); + break; + + case DW_AT_call_line: + if (call_line == 0) + call_line = form_value.Unsigned(); + break; + + case DW_AT_call_column: + if (call_column == 0) + call_column = form_value.Unsigned(); + break; + + case DW_AT_frame_base: + if (frame_base) + { + if (form_value.BlockData()) + { + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + frame_base->SetOpcodeData(debug_info_data, block_offset, block_length, NULL); + } + else + { + const DataExtractor& debug_loc_data = dwarf2Data->get_debug_loc_data(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) + { + Address base_address(cu->GetBaseAddress(), dwarf2Data->GetObjectFile()->GetSectionList()); + frame_base->SetOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length, &base_address); + } + } + } + break; + + default: + break; + } + } + } + } + + size_t numRanges = ranges.Size(); + + if (numRanges == 0) + { + if (lo_pc != DW_INVALID_ADDRESS) + { + if (hi_pc != DW_INVALID_ADDRESS) + ranges.AddRange(lo_pc, hi_pc); + else + ranges.AddRange(lo_pc, lo_pc); + } + } + + if (ranges.Size() == 0 || (name == NULL) || (mangled == NULL)) + { + std::vector::const_iterator pos; + std::vector::const_iterator end = die_offsets.end(); + for (pos = die_offsets.begin(); pos != end; ++pos) + { + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = NULL; + dw_offset_t die_offset = *pos; + if (die_offset != DW_INVALID_OFFSET) + { + die = dwarf2Data->DebugInfo()->GetDIEPtr(die_offset, &cu_sp_ptr); + if (die) + die->GetDIENamesAndRanges(dwarf2Data, cu_sp_ptr.get(), name, mangled, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column); + } + } + } + return ranges.Size() > 0; +} + +//---------------------------------------------------------------------- +// Dump +// +// Dumps a debug information entry and all of it's attributes to the +// specified stream. +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::Dump +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + Stream *s, + uint32_t recurse_depth +) const +{ + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + uint32_t offset = m_offset; + bool english = s->GetFlags().IsSet (DWARFDebugInfo::eDumpFlag_EnglishyNames); + + if (debug_info_data.ValidOffset(offset)) + { + dw_uleb128_t abbrCode = debug_info_data.GetULEB128(&offset); + + s->Printf("\n0x%8.8x: ", m_offset); + s->Indent(); + if (abbrCode) + { + if (m_abbrevDecl) + { + if (english) + s->PutCString(DW_TAG_value_to_englishy_name(m_abbrevDecl->Tag())); + else + s->PutCString(DW_TAG_value_to_name(m_abbrevDecl->Tag())); + s->Printf( " [%u] %c\n", abbrCode, m_abbrevDecl->HasChildren() ? '*':' '); + + // Dump all data in the .debug_info for the attributes + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; iGetAttrAndFormByIndexUnchecked(i, attr, form); + + DumpAttribute(dwarf2Data, cu, debug_info_data, &offset, s, attr, form); + } + + const DWARFDebugInfoEntry* child = GetFirstChild(); + if (recurse_depth > 0 && child) + { + s->IndentMore(); + + while (child) + { + child->Dump(dwarf2Data, cu, s, recurse_depth-1); + child = child->GetSibling(); + } + s->IndentLess(); + } + } + else + s->Printf( "Abbreviation code note found in 'debug_abbrev' class for code: %u\n", abbrCode); + } + else + { + s->Printf( "NULL\n"); + } + } +} + +//---------------------------------------------------------------------- +// DumpAttribute +// +// Dumps a debug information entry attribute along with it's form. Any +// special display of attributes is done (disassemble location lists, +// show enumeration values for attributes, etc). +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::DumpAttribute +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DataExtractor& debug_info_data, + uint32_t* offset_ptr, + Stream *s, + dw_attr_t attr, + dw_form_t form +) +{ + bool verbose = s->GetVerbose(); + bool show_form = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_ShowForm); + bool english = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_EnglishyNames); + const DataExtractor* debug_str_data = dwarf2Data ? &dwarf2Data->get_debug_str_data() : NULL; + if (verbose) + s->Offset(*offset_ptr); + else + s->Printf( " "); + s->Indent(); + + if (english) + s->PutCString(DW_AT_value_to_englishy_name(attr)); + else + s->PutCString(DW_AT_value_to_name(attr)); + + if (show_form) + { + s->Printf( "[%s", english ? DW_FORM_value_to_englishy_name(form) : DW_FORM_value_to_name(form)); + } + + DWARFFormValue form_value(form); + + if (!form_value.ExtractValue(debug_info_data, offset_ptr, cu)) + return; + + if (show_form) + { + if (form == DW_FORM_indirect) + { + s->Printf( " [%s]", english ? DW_FORM_value_to_englishy_name(form_value.Form()) : DW_FORM_value_to_name(form_value.Form())); + } + + s->PutCString("] "); + } + + s->PutCString("( "); + + // Always dump form value if verbose is enabled + if (verbose) + { + form_value.Dump(s, debug_str_data, cu); + } + + + // Check to see if we have any special attribute formatters + switch (attr) + { + case DW_AT_stmt_list: + if ( verbose ) s->PutCString(" ( "); + s->Printf( "0x%8.8x", form_value.Unsigned()); + if ( verbose ) s->PutCString(" )"); + break; + + case DW_AT_language: + if ( verbose ) s->PutCString(" ( "); + s->PutCString(DW_LANG_value_to_name(form_value.Unsigned())); + if ( verbose ) s->PutCString(" )"); + break; + + case DW_AT_encoding: + if ( verbose ) s->PutCString(" ( "); + s->PutCString(DW_ATE_value_to_name(form_value.Unsigned())); + if ( verbose ) s->PutCString(" )"); + break; + + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + { + const uint8_t* blockData = form_value.BlockData(); + if (blockData) + { + if (!verbose) + form_value.Dump(s, debug_str_data, cu); + + // Location description is inlined in data in the form value + DataExtractor locationData(debug_info_data, (*offset_ptr) - form_value.Unsigned(), form_value.Unsigned()); + if ( verbose ) s->PutCString(" ( "); + print_dwarf_expression (s, locationData, DWARFCompileUnit::GetAddressByteSize(cu), 4, false); + if ( verbose ) s->PutCString(" )"); + } + else + { + // We have a location list offset as the value that is + // the offset into the .debug_loc section that describes + // the value over it's lifetime + uint64_t debug_loc_offset = form_value.Unsigned(); + if (dwarf2Data) + { + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + DWARFLocationList::Dump(s, cu, dwarf2Data->get_debug_loc_data(), debug_loc_offset); + } + else + { + if ( !verbose ) + form_value.Dump(s, NULL, cu); + } + } + } + break; + + case DW_AT_abstract_origin: + case DW_AT_specification: + { + uint64_t abstract_die_offset = form_value.Reference(cu); + form_value.Dump(s, debug_str_data, cu); + // *ostrm_ptr << HEX32 << abstract_die_offset << " ( "; + if ( verbose ) s->PutCString(" ( "); + GetName(dwarf2Data, cu, abstract_die_offset, s); + if ( verbose ) s->PutCString(" )"); + } + break; + + case DW_AT_type: + { + uint64_t type_die_offset = form_value.Reference(cu); + if (!verbose) + form_value.Dump(s, debug_str_data, cu); + s->PutCString(" ( "); + AppendTypeName(dwarf2Data, cu, type_die_offset, s); + s->PutCString(" )"); + } + break; + + case DW_AT_ranges: + { + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + uint32_t ranges_offset = form_value.Unsigned(); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + DWARFDebugRanges::Dump(s, dwarf2Data->get_debug_ranges_data(), &ranges_offset, base_addr); + } + break; + + default: + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + break; + } + + s->PutCString(" )\n"); +} + +//---------------------------------------------------------------------- +// Get all attribute values for a given DIE, including following any +// specification or abstract origin attributes and including those in +// the results. Any duplicate attributes will have the first instance +// take precedence (this can happen for declaration attributes). +//---------------------------------------------------------------------- +size_t +DWARFDebugInfoEntry::GetAttributes +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry::Attributes& attributes +) const +{ + if (m_abbrevDecl) + { + uint32_t offset = GetOffset(); + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + // Skip the abbreviation code so we are at the data for the attributes + debug_info_data.Skip_LEB128(&offset); + + const uint32_t num_attributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + DWARFFormValue form_value; + for (i=0; iGetAttrAndFormByIndexUnchecked (i, attr, form); + attributes.Append(cu, offset, attr, form); + if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) + { + form_value.SetForm(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + const DWARFDebugInfoEntry* die = NULL; + dw_offset_t die_offset = form_value.Reference(cu); + if (cu->ContainsDIEOffset(die_offset)) + { + die = const_cast(cu)->GetDIEPtr(die_offset); + if (die) + die->GetAttributes(dwarf2Data, cu, attributes); + } + else + { + DWARFCompileUnitSP cu_sp_ptr; + die = const_cast(dwarf2Data)->DebugInfo()->GetDIEPtr(die_offset, &cu_sp_ptr); + if (die) + die->GetAttributes(dwarf2Data, cu_sp_ptr.get(), attributes); + } + } + } + else + { + assert(DWARFFormValue::SkipValue(form, debug_info_data, &offset, cu)); + } + } + } + else + { + attributes.Clear(); + } + return attributes.Size(); + +} + +//---------------------------------------------------------------------- +// GetAttributeValue +// +// Get the value of an attribute and return the .debug_info offset of the +// attribute if it was properly extracted into form_value, or zero +// if we fail since an offset of zero is invalid for an attribute (it +// would be a compile unit header). +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugInfoEntry::GetAttributeValue +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DWARFFormValue& form_value, + dw_offset_t* end_attr_offset_ptr +) const +{ + if (m_abbrevDecl) + { + uint32_t attr_idx = m_abbrevDecl->FindAttributeIndex(attr); + + if (attr_idx != DW_INVALID_INDEX) + { + uint32_t offset = GetOffset(); + + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + // Skip the abbreviation code so we are at the data for the attributes + debug_info_data.Skip_LEB128(&offset); + + uint32_t idx=0; + while (idxGetFormByIndex(idx++), debug_info_data, &offset, cu); + + const dw_offset_t attr_offset = offset; + form_value.SetForm(m_abbrevDecl->GetFormByIndex(idx)); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + if (end_attr_offset_ptr) + *end_attr_offset_ptr = offset; + return attr_offset; + } + } + } + + return 0; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsString +// +// Get the value of an attribute as a string return it. The resulting +// pointer to the string data exists within the supplied SymbolFileDWARF +// and will only be available as long as the SymbolFileDWARF is still around +// and it's content doesn't change. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetAttributeValueAsString +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + const char* fail_value) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.AsCString(&dwarf2Data->get_debug_str_data()); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsUnsigned +// +// Get the value of an attribute as unsigned and return it. +//---------------------------------------------------------------------- +uint64_t +DWARFDebugInfoEntry::GetAttributeValueAsUnsigned +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Unsigned(); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsSigned +// +// Get the value of an attribute a signed value and return it. +//---------------------------------------------------------------------- +int64_t +DWARFDebugInfoEntry::GetAttributeValueAsSigned +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + int64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Signed(); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsReference +// +// Get the value of an attribute as reference and fix up and compile +// unit relative offsets as needed. +//---------------------------------------------------------------------- +uint64_t +DWARFDebugInfoEntry::GetAttributeValueAsReference +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Reference(cu); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsLocation +// +// Get the value of an attribute as reference and fix up and compile +// unit relative offsets as needed. +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugInfoEntry::GetAttributeValueAsLocation +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DataExtractor& location_data, + uint32_t &block_size +) const +{ + block_size = 0; + DWARFFormValue form_value; + + // Empty out data in case we don't find anything + location_data.Clear(); + dw_offset_t end_addr_offset = DW_INVALID_OFFSET; + const dw_offset_t attr_offset = GetAttributeValue(dwarf2Data, cu, attr, form_value, &end_addr_offset); + if (attr_offset) + { + const uint8_t* blockData = form_value.BlockData(); + if (blockData) + { + // We have an inlined location list in the .debug_info section + const DataExtractor& debug_info = dwarf2Data->get_debug_info_data(); + dw_offset_t block_offset = blockData - debug_info.GetDataStart(); + block_size = (end_addr_offset - attr_offset) - form_value.Unsigned(); + location_data.SetData(debug_info, block_offset, block_size); + } + else + { + // We have a location list offset as the value that is + // the offset into the .debug_loc section that describes + // the value over it's lifetime + dw_offset_t debug_loc_offset = form_value.Unsigned(); + if (dwarf2Data) + { + assert(dwarf2Data->get_debug_loc_data().GetAddressByteSize() == cu->GetAddressByteSize()); + return DWARFLocationList::Extract(dwarf2Data->get_debug_loc_data(), &debug_loc_offset, location_data); + } + } + } + return attr_offset; +} + +//---------------------------------------------------------------------- +// GetName +// +// Get value of the DW_AT_name attribute and return it if one exists, +// else return NULL. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + return form_value.AsCString(&dwarf2Data->get_debug_str_data()); + return NULL; +} + + +//---------------------------------------------------------------------- +// GetMangledName +// +// Get value of the DW_AT_MIPS_linkage_name attribute and return it if +// one exists, else return the value of the DW_AT_name attribute +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetMangledName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + bool substitute_name_allowed +) const +{ + const char* name = NULL; + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_MIPS_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + + if (substitute_name_allowed && name == NULL) + { + if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + } + return name; +} + + +//---------------------------------------------------------------------- +// GetPubname +// +// Get value the name for a DIE as it should appear for a +// .debug_pubnames or .debug_pubtypes section. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetPubname +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu +) const +{ + const char* name = NULL; + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_MIPS_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_specification, form_value)) + { + // The specification DIE may be in another compile unit so we need + // to get a die and its compile unit. + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = const_cast(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + if (die) + return die->GetPubname(dwarf2Data, cu_sp_ptr.get()); + } + return name; +} + + +//---------------------------------------------------------------------- +// GetName +// +// Get value of the DW_AT_name attribute for a debug information entry +// that exists at offset "die_offset" and place that value into the +// supplied stream object. If the DIE is a NULL object "NULL" is placed +// into the stream, and if no DW_AT_name attribute exists for the DIE +// then nothing is printed. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const uint32_t die_offset, + Stream *s +) +{ + DWARFDebugInfoEntry die; + uint32_t offset = die_offset; + if (die.Extract(dwarf2Data, cu, &offset)) + { + if (die.IsNULL()) + { + s->PutCString("NULL"); + return true; + } + else + { + DWARFFormValue form_value; + if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + { + const char* name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + if (name) + { + s->PutCString(name); + return true; + } + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// AppendTypeName +// +// Follows the type name definition down through all needed tags to +// end up with a fully qualified type name and dump the results to +// the supplied stream. This is used to show the name of types given +// a type identifier. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::AppendTypeName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const uint32_t die_offset, + Stream *s +) +{ + DWARFDebugInfoEntry die; + uint32_t offset = die_offset; + if (die.Extract(dwarf2Data, cu, &offset)) + { + if (die.IsNULL()) + { + s->PutCString("NULL"); + return true; + } + else + { + const char* name = die.GetPubname(dwarf2Data, cu); + // if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + // name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + if (name) + s->PutCString(name); + else + { + bool result = true; + const DWARFAbbreviationDeclaration* abbrevDecl = die.GetAbbreviationDeclarationPtr(); + + switch (abbrevDecl->Tag()) + { + case DW_TAG_array_type: break; // print out a "[]" after printing the full type of the element below + case DW_TAG_base_type: s->PutCString("base "); break; + case DW_TAG_class_type: s->PutCString("class "); break; + case DW_TAG_const_type: s->PutCString("const "); break; + case DW_TAG_enumeration_type: s->PutCString("enum "); break; + case DW_TAG_file_type: s->PutCString("file "); break; + case DW_TAG_interface_type: s->PutCString("interface "); break; + case DW_TAG_packed_type: s->PutCString("packed "); break; + case DW_TAG_pointer_type: break; // print out a '*' after printing the full type below + case DW_TAG_ptr_to_member_type: break; // print out a '*' after printing the full type below + case DW_TAG_reference_type: break; // print out a '&' after printing the full type below + case DW_TAG_restrict_type: s->PutCString("restrict "); break; + case DW_TAG_set_type: s->PutCString("set "); break; + case DW_TAG_shared_type: s->PutCString("shared "); break; + case DW_TAG_string_type: s->PutCString("string "); break; + case DW_TAG_structure_type: s->PutCString("struct "); break; + case DW_TAG_subrange_type: s->PutCString("subrange "); break; + case DW_TAG_subroutine_type: s->PutCString("function "); break; + case DW_TAG_thrown_type: s->PutCString("thrown "); break; + case DW_TAG_union_type: s->PutCString("union "); break; + case DW_TAG_unspecified_type: s->PutCString("unspecified "); break; + case DW_TAG_volatile_type: s->PutCString("volatile "); break; + default: + return false; + } + + // Follow the DW_AT_type if possible + DWARFFormValue form_value; + if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_type, form_value)) + { + uint64_t next_die_offset = form_value.Reference(cu); + result = AppendTypeName(dwarf2Data, cu, next_die_offset, s); + } + + switch (abbrevDecl->Tag()) + { + case DW_TAG_array_type: s->PutCString("[]"); break; + case DW_TAG_pointer_type: s->PutChar('*'); break; + case DW_TAG_ptr_to_member_type: s->PutChar('*'); break; + case DW_TAG_reference_type: s->PutChar('&'); break; + default: + break; + } + return result; + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// BuildAddressRangeTable +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::BuildAddressRangeTable +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges +) const +{ + if (m_abbrevDecl) + { + dw_tag_t tag = m_abbrevDecl->Tag(); + if (tag == DW_TAG_subprogram) + { + dw_addr_t hi_pc = DW_INVALID_ADDRESS; + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (lo_pc != DW_INVALID_ADDRESS) + hi_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (hi_pc != DW_INVALID_ADDRESS) + { + /// printf("BuildAddressRangeTable() 0x%8.8x: %30s: [0x%8.8x - 0x%8.8x)\n", m_offset, DW_TAG_value_to_name(tag), lo_pc, hi_pc); + debug_aranges->InsertRange(cu->GetOffset(), lo_pc, hi_pc); + } + } + + + const DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + child->BuildAddressRangeTable(dwarf2Data, cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +//---------------------------------------------------------------------- +// BuildFunctionAddressRangeTable +// +// This function is very similar to the BuildAddressRangeTable function +// except that the actual DIE offset for the function is placed in the +// table instead of the compile unit offset (which is the way the +// standard .debug_aranges section does it). +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::BuildFunctionAddressRangeTable +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges +) const +{ + if (m_abbrevDecl) + { + dw_tag_t tag = m_abbrevDecl->Tag(); + if (tag == DW_TAG_subprogram) + { + dw_addr_t hi_pc = DW_INVALID_ADDRESS; + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (lo_pc != DW_INVALID_ADDRESS) + hi_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (hi_pc != DW_INVALID_ADDRESS) + { + // printf("BuildAddressRangeTable() 0x%8.8x: [0x%16.16llx - 0x%16.16llx)\n", m_offset, lo_pc, hi_pc); // DEBUG ONLY + debug_aranges->InsertRange(GetOffset(), lo_pc, hi_pc); + } + } + + const DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + child->BuildFunctionAddressRangeTable(dwarf2Data, cu, debug_aranges); + child = child->GetSibling(); + } + } +} + + +//---------------------------------------------------------------------- +// LookupAddress +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::LookupAddress +( + const dw_addr_t address, + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die +) +{ + bool found_address = false; + if (m_abbrevDecl) + { + bool check_children = false; + bool match_addr_range = false; + dw_tag_t tag = m_abbrevDecl->Tag(); + // printf("0x%8.8x: %30s: address = 0x%8.8x - ", m_offset, DW_TAG_value_to_name(tag), address); + switch (tag) + { + case DW_TAG_array_type : break; + case DW_TAG_class_type : check_children = true; break; + case DW_TAG_entry_point : break; + case DW_TAG_enumeration_type : break; + case DW_TAG_formal_parameter : break; + case DW_TAG_imported_declaration : break; + case DW_TAG_label : break; + case DW_TAG_lexical_block : check_children = true; match_addr_range = true; break; + case DW_TAG_member : break; + case DW_TAG_pointer_type : break; + case DW_TAG_reference_type : break; + case DW_TAG_compile_unit : match_addr_range = true; break; + case DW_TAG_string_type : break; + case DW_TAG_structure_type : check_children = true; break; + case DW_TAG_subroutine_type : break; + case DW_TAG_typedef : break; + case DW_TAG_union_type : break; + case DW_TAG_unspecified_parameters : break; + case DW_TAG_variant : break; + case DW_TAG_common_block : check_children = true; break; + case DW_TAG_common_inclusion : break; + case DW_TAG_inheritance : break; + case DW_TAG_inlined_subroutine : check_children = true; match_addr_range = true; break; + case DW_TAG_module : match_addr_range = true; break; + case DW_TAG_ptr_to_member_type : break; + case DW_TAG_set_type : break; + case DW_TAG_subrange_type : break; + case DW_TAG_with_stmt : break; + case DW_TAG_access_declaration : break; + case DW_TAG_base_type : break; + case DW_TAG_catch_block : match_addr_range = true; break; + case DW_TAG_const_type : break; + case DW_TAG_constant : break; + case DW_TAG_enumerator : break; + case DW_TAG_file_type : break; + case DW_TAG_friend : break; + case DW_TAG_namelist : break; + case DW_TAG_namelist_item : break; + case DW_TAG_packed_type : break; + case DW_TAG_subprogram : match_addr_range = true; break; + case DW_TAG_template_type_parameter : break; + case DW_TAG_template_value_parameter : break; + case DW_TAG_thrown_type : break; + case DW_TAG_try_block : match_addr_range = true; break; + case DW_TAG_variant_part : break; + case DW_TAG_variable : break; + case DW_TAG_volatile_type : break; + case DW_TAG_dwarf_procedure : break; + case DW_TAG_restrict_type : break; + case DW_TAG_interface_type : break; + case DW_TAG_namespace : check_children = true; break; + case DW_TAG_imported_module : break; + case DW_TAG_unspecified_type : break; + case DW_TAG_partial_unit : break; + case DW_TAG_imported_unit : break; + case DW_TAG_shared_type : break; + default: break; + } + + if (match_addr_range) + { + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (lo_pc != DW_INVALID_ADDRESS) + { + dw_addr_t hi_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (hi_pc != DW_INVALID_ADDRESS) + { + // printf("\n0x%8.8x: %30s: address = 0x%8.8x [0x%8.8x - 0x%8.8x) ", m_offset, DW_TAG_value_to_name(tag), address, lo_pc, hi_pc); + if ((lo_pc <= address) && (address < hi_pc)) + { + found_address = true; + // puts("***MATCH***"); + switch (tag) + { + case DW_TAG_compile_unit: // File + check_children = ((function_die != NULL) || (block_die != NULL)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != NULL); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) + { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + } + else + { // compile units may not have a valid high/low pc when there + // are address gaps in subtroutines so we must always search + // if there is no valid high and low PC + check_children = (tag == DW_TAG_compile_unit) && ((function_die != NULL) || (block_die != NULL)); + } + } + else + { + dw_offset_t debug_ranges_offset = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_ranges, DW_INVALID_OFFSET); + if (debug_ranges_offset != DW_INVALID_OFFSET) + { + DWARFDebugRanges::RangeList ranges; + DWARFDebugRanges* debug_ranges = dwarf2Data->DebugRanges(); + debug_ranges->FindRanges(debug_ranges_offset, ranges); + // All DW_AT_ranges are relative to the base address of the + // compile unit. We add the compile unit base address to make + // sure all the addresses are properly fixed up. + ranges.AddOffset(cu->GetBaseAddress()); + if (ranges.Lookup(address)) + { + found_address = true; + // puts("***MATCH***"); + switch (tag) + { + case DW_TAG_compile_unit: // File + check_children = ((function_die != NULL) || (block_die != NULL)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != NULL); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) + { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + else + { + check_children = false; + } + } + } + } + + + if (check_children) + { + // printf("checking children\n"); + DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + if (child->LookupAddress(address, dwarf2Data, cu, function_die, block_die)) + return true; + child = child->GetSibling(); + } + } + } + return found_address; +} + + +bool +DWARFDebugInfoEntry::OffsetLessThan (const DWARFDebugInfoEntry& a, const DWARFDebugInfoEntry& b) +{ + return a.GetOffset() < b.GetOffset(); +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h new file mode 100644 index 000000000000..8340acc427e9 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -0,0 +1,320 @@ +//===-- DWARFDebugInfoEntry.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugInfoEntry_h_ +#define liblldb_DWARFDebugInfoEntry_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFAbbreviationDeclaration.h" +#include "DWARFDebugRanges.h" +#include +#include +#include + +typedef std::map DIEToAddressMap; +typedef DIEToAddressMap::iterator DIEToAddressMapIter; +typedef DIEToAddressMap::const_iterator DIEToAddressMapConstIter; + +typedef std::map AddressToDIEMap; +typedef AddressToDIEMap::iterator AddressToDIEMapIter; +typedef AddressToDIEMap::const_iterator AddressToDIEMapConstIter; + + +typedef std::map DIEToDIEMap; +typedef DIEToDIEMap::iterator DIEToDIEMapIter; +typedef DIEToDIEMap::const_iterator DIEToDIEMapConstIter; + +typedef std::map UInt32ToDIEMap; +typedef UInt32ToDIEMap::iterator UInt32ToDIEMapIter; +typedef UInt32ToDIEMap::const_iterator UInt32ToDIEMapConstIter; + +typedef std::multimap UInt32ToDIEMMap; +typedef UInt32ToDIEMMap::iterator UInt32ToDIEMMapIter; +typedef UInt32ToDIEMMap::const_iterator UInt32ToDIEMMapConstIter; + +class DWARFDebugInfoEntry +{ +public: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + typedef std::vector offset_collection; + typedef offset_collection::iterator offset_collection_iterator; + typedef offset_collection::const_iterator offset_collection_const_iterator; + + class Attributes + { + public: + Attributes(); + ~Attributes(); + + void Append(const DWARFCompileUnit *cu, dw_offset_t attr_die_offset, dw_attr_t attr, dw_form_t form); + const DWARFCompileUnit * CompileUnitAtIndex(uint32_t i) const { return m_infos[i].cu; } + dw_offset_t DIEOffsetAtIndex(uint32_t i) const { return m_infos[i].die_offset; } + dw_attr_t AttributeAtIndex(uint32_t i) const { return m_infos[i].attr; } + dw_attr_t FormAtIndex(uint32_t i) const { return m_infos[i].form; } + bool ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const; + uint64_t FormValueAsUnsignedAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, uint64_t fail_value) const; + uint32_t FindAttributeIndex(dw_attr_t attr) const; + bool ContainsAttribute(dw_attr_t attr) const; + bool RemoveAttribute(dw_attr_t attr); + void Clear() { m_infos.clear(); } + uint32_t Size() const { return m_infos.size(); } + + protected: + struct Info + { + const DWARFCompileUnit *cu; // Keep the compile unit with each attribute in case we have DW_FORM_ref_addr values + dw_offset_t die_offset; + dw_attr_t attr; + dw_form_t form; + }; + std::vector m_infos; + }; + + struct CompareState + { + CompareState() : + die_offset_pairs() + { + assert(sizeof(dw_offset_t)*2 == sizeof(uint64_t)); + } + + bool AddTypePair(dw_offset_t a, dw_offset_t b) + { + uint64_t a_b_offsets = (uint64_t)a << 32 | (uint64_t)b; + // Return true if this type was inserted, false otherwise + return die_offset_pairs.insert(a_b_offsets).second; + } + std::set< uint64_t > die_offset_pairs; + }; + + DWARFDebugInfoEntry(): + m_offset (DW_INVALID_OFFSET), + m_parent_idx (0), + m_sibling_idx (0), + m_abbrevDecl (NULL), + m_user_data (NULL) + { + } + + + void BuildAddressRangeTable( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges) const; + + void BuildFunctionAddressRangeTable( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges) const; + + bool Extract( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + dw_offset_t* offset_ptr); + + bool LookupAddress( + const dw_addr_t address, + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + size_t GetAttributes( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry::Attributes& attrs) const; + + dw_offset_t GetAttributeValue( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DWARFFormValue& formValue, + dw_offset_t* end_attr_offset_ptr = NULL) const; + + const char* GetAttributeValueAsString( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + const char* fail_value) const; + + uint64_t GetAttributeValueAsUnsigned( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value) const; + + uint64_t GetAttributeValueAsReference( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value) const; + + int64_t GetAttributeValueAsSigned( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + int64_t fail_value) const; + + dw_offset_t GetAttributeValueAsLocation( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + lldb_private::DataExtractor& data, + uint32_t &block_size) const; + + const char* GetName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu) const; + + const char* GetMangledName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + bool substitute_name_allowed = true) const; + + const char* GetPubname( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu) const; + + static bool GetName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + lldb_private::Stream *s); + + static bool AppendTypeName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + lldb_private::Stream *s); + + static int Compare( + SymbolFileDWARF* dwarf2Data, + dw_offset_t a_die_offset, + dw_offset_t b_die_offset, + CompareState &compare_state, + bool compare_siblings, + bool compare_children); + + static int Compare( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* a_cu, const DWARFDebugInfoEntry* a_die, + DWARFCompileUnit* b_cu, const DWARFDebugInfoEntry* b_die, + CompareState &compare_state, + bool compare_siblings, + bool compare_children); + + static bool OffsetLessThan ( + const DWARFDebugInfoEntry& a, + const DWARFDebugInfoEntry& b); + + bool AppendDependentDIES( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const bool add_children, + DWARFDIECollection& die_offsets) const; + + void Dump( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + lldb_private::Stream *s, + uint32_t recurse_depth) const; + + void DumpAncestry( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* oldest, + lldb_private::Stream *s, + uint32_t recurse_depth) const; + + static void DumpAttribute( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const lldb_private::DataExtractor& debug_info_data, + uint32_t* offset_ptr, + lldb_private::Stream *s, + dw_attr_t attr, + dw_form_t form); + + bool GetDIENamesAndRanges( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const char * &name, + const char * &mangled, + DWARFDebugRanges::RangeList& rangeList, + int& decl_file, + int& decl_line, + int& decl_column, + int& call_file, + int& call_line, + int& call_column, + lldb_private::DWARFExpression *frame_base = NULL) const; + + + dw_tag_t Tag() const { return m_abbrevDecl ? m_abbrevDecl->Tag() : 0; } + bool IsNULL() const { return m_abbrevDecl == NULL; } + dw_offset_t GetOffset() const { return m_offset; } + void SetOffset(dw_offset_t offset) { m_offset = offset; } + uint32_t NumAttributes() const { return m_abbrevDecl ? m_abbrevDecl->NumAttributes() : 0; } + bool HasChildren() const { return m_abbrevDecl != NULL && m_abbrevDecl->HasChildren(); } + + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + DWARFDebugInfoEntry* GetParent() { return m_parent_idx > 0 ? this - m_parent_idx : NULL; } + const DWARFDebugInfoEntry* GetParent() const { return m_parent_idx > 0 ? this - m_parent_idx : NULL; } + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + DWARFDebugInfoEntry* GetSibling() { return m_sibling_idx > 0 ? this + m_sibling_idx : NULL; } + const DWARFDebugInfoEntry* GetSibling() const { return m_sibling_idx > 0 ? this + m_sibling_idx : NULL; } + // We know we are kept in a vector of contiguous entries, so we know + // we don't need to store our child pointer, if we have a child it will + // be the next entry in the list... + DWARFDebugInfoEntry* GetFirstChild() { return HasChildren() ? this + 1 : NULL; } + const DWARFDebugInfoEntry* GetFirstChild() const { return HasChildren() ? this + 1 : NULL; } + + void + SetParent (DWARFDebugInfoEntry* parent) + { + if (parent) + { + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + m_parent_idx = this - parent; + } + else + m_parent_idx = 0; + } + void + SetSibling (DWARFDebugInfoEntry* sibling) + { + if (sibling) + { + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + m_sibling_idx = sibling - this; + sibling->SetParent(GetParent()); + } + else + m_sibling_idx = 0; + } + const DWARFAbbreviationDeclaration* GetAbbreviationDeclarationPtr() const { return m_abbrevDecl; } + + void * GetUserData() const { return m_user_data; } + void SetUserData(void *d) const { m_user_data = d; } +protected: + dw_offset_t m_offset; // Offset within the .debug_info of the start of this entry + uint32_t m_parent_idx; // How many to subtract from "this" to get the parent. If zero this die has no parent + uint32_t m_sibling_idx; // How many to add to "this" to get the sibling. + const DWARFAbbreviationDeclaration* m_abbrevDecl; + mutable void * m_user_data; // Flags for use by the parsers +}; + +#endif // liblldb_DWARFDebugInfoEntry_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp new file mode 100644 index 000000000000..2b3f39b24f36 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,1410 @@ +//===-- DWARFDebugLine.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugLine.h" + +//#define ENABLE_DEBUG_PRINTF // DO NOT LEAVE THIS DEFINED: DEBUG ONLY!!! +#include + +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" + +#include "SymbolFileDWARF.h" +#include "LogChannelDWARF.h" + +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// Parse +// +// Parse all information in the debug_line_data into an internal +// representation. +//---------------------------------------------------------------------- +void +DWARFDebugLine::Parse(const DataExtractor& debug_line_data) +{ + m_lineTableMap.clear(); + dw_offset_t offset = 0; + LineTable::shared_ptr line_table_sp(new LineTable); + while (debug_line_data.ValidOffset(offset)) + { + const uint32_t debug_line_offset = offset; + + if (line_table_sp.get() == NULL) + break; + + if (ParseStatementTable(debug_line_data, &offset, line_table_sp.get())) + { + // Make sure we don't don't loop infinitely + if (offset <= debug_line_offset) + break; + //DEBUG_PRINTF("m_lineTableMap[0x%8.8x] = line_table_sp\n", debug_line_offset); + m_lineTableMap[debug_line_offset] = line_table_sp; + line_table_sp.reset(new LineTable); + } + else + ++offset; // Try next byte in line table + } +} + +void +DWARFDebugLine::ParseIfNeeded(const DataExtractor& debug_line_data) +{ + if (m_lineTableMap.empty()) + Parse(debug_line_data); +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::GetLineTable +//---------------------------------------------------------------------- +DWARFDebugLine::LineTable::shared_ptr +DWARFDebugLine::GetLineTable(const dw_offset_t offset) const +{ + DWARFDebugLine::LineTable::shared_ptr line_table_shared_ptr; + LineTableConstIter pos = m_lineTableMap.find(offset); + if (pos != m_lineTableMap.end()) + line_table_shared_ptr = pos->second; + return line_table_shared_ptr; +} + + +//---------------------------------------------------------------------- +// DumpStateToFile +//---------------------------------------------------------------------- +static void +DumpStateToFile (dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + Log *log = (Log *)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // If the row is zero we are being called with the prologue only + state.prologue->Dump (log); + log->PutCString ("Address Line Column File"); + log->PutCString ("------------------ ------ ------ ------"); + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table + } + else + { + log->Printf( "0x%16.16llx %6u %6u %6u%s\n", state.address, state.line, state.column, state.file, state.end_sequence ? " END" : ""); + } +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::DumpLineTableRows +//---------------------------------------------------------------------- +bool +DWARFDebugLine::DumpLineTableRows(Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t debug_line_offset) +{ + const DataExtractor& debug_line_data = dwarf2Data->get_debug_line_data(); + + if (debug_line_offset == DW_INVALID_OFFSET) + { + // Dump line table to a single file only + debug_line_offset = 0; + while (debug_line_data.ValidOffset(debug_line_offset)) + debug_line_offset = DumpStatementTable (log, debug_line_data, debug_line_offset); + } + else + { + // Dump line table to a single file only + DumpStatementTable (log, debug_line_data, debug_line_offset); + } + return false; +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::DumpStatementTable +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugLine::DumpStatementTable(Log *log, const DataExtractor& debug_line_data, const dw_offset_t debug_line_offset) +{ + if (debug_line_data.ValidOffset(debug_line_offset)) + { + uint32_t offset = debug_line_offset; + log->Printf( "----------------------------------------------------------------------\n" + "debug_line[0x%8.8x]\n" + "----------------------------------------------------------------------\n", debug_line_offset); + + if (ParseStatementTable(debug_line_data, &offset, DumpStateToFile, log)) + return offset; + else + return debug_line_offset + 1; // Skip to next byte in .debug_line section + } + + return DW_INVALID_OFFSET; +} + + +//---------------------------------------------------------------------- +// DumpOpcodes +//---------------------------------------------------------------------- +bool +DWARFDebugLine::DumpOpcodes(Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t debug_line_offset, uint32_t dump_flags) +{ + const DataExtractor& debug_line_data = dwarf2Data->get_debug_line_data(); + + if (debug_line_data.GetByteSize() == 0) + { + log->Printf( "< EMPTY >\n"); + return false; + } + + if (debug_line_offset == DW_INVALID_OFFSET) + { + // Dump line table to a single file only + debug_line_offset = 0; + while (debug_line_data.ValidOffset(debug_line_offset)) + debug_line_offset = DumpStatementOpcodes (log, debug_line_data, debug_line_offset, dump_flags); + } + else + { + // Dump line table to a single file only + DumpStatementOpcodes (log, debug_line_data, debug_line_offset, dump_flags); + } + return false; +} + +//---------------------------------------------------------------------- +// DumpStatementOpcodes +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugLine::DumpStatementOpcodes(Log *log, const DataExtractor& debug_line_data, const dw_offset_t debug_line_offset, uint32_t flags) +{ + uint32_t offset = debug_line_offset; + if (debug_line_data.ValidOffset(offset)) + { + Prologue prologue; + + if (ParsePrologue(debug_line_data, &offset, &prologue)) + { + log->PutCString ("----------------------------------------------------------------------"); + log->Printf ("debug_line[0x%8.8x]", debug_line_offset); + log->PutCString ("----------------------------------------------------------------------\n"); + prologue.Dump (log); + } + else + { + offset = debug_line_offset; + log->Printf( "0x%8.8x: skipping pad byte %2.2x", offset, debug_line_data.GetU8(&offset)); + return offset; + } + + Row row(prologue.default_is_stmt); + const dw_offset_t end_offset = debug_line_offset + prologue.total_length + sizeof(prologue.total_length); + + assert(debug_line_data.ValidOffset(end_offset-1)); + + while (offset < end_offset) + { + const uint32_t op_offset = offset; + uint8_t opcode = debug_line_data.GetU8(&offset); + switch (opcode) + { + case 0: // Extended Opcodes always start with a zero opcode followed by + { // a uleb128 length so you can skip ones you don't know about + + dw_offset_t ext_offset = offset; + dw_uleb128_t len = debug_line_data.GetULEB128(&offset); + dw_offset_t arg_size = len - (offset - ext_offset); + uint8_t sub_opcode = debug_line_data.GetU8(&offset); +// if (verbose) +// log->Printf( "Extended: <%u> %2.2x ", len, sub_opcode); + + switch (sub_opcode) + { + case DW_LNE_end_sequence : + log->Printf( "0x%8.8x: DW_LNE_end_sequence", op_offset); + row.Dump(log); + row.Reset(prologue.default_is_stmt); + break; + + case DW_LNE_set_address : + { + row.address = debug_line_data.GetMaxU64(&offset, arg_size); + log->Printf( "0x%8.8x: DW_LNE_set_address (0x%llx)", op_offset, row.address); + } + break; + + case DW_LNE_define_file: + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(&offset); + fileEntry.dir_idx = debug_line_data.GetULEB128(&offset); + fileEntry.mod_time = debug_line_data.GetULEB128(&offset); + fileEntry.length = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNE_define_file('%s', dir=%i, mod_time=0x%8.8x, length=%i )", + op_offset, + fileEntry.name.c_str(), + fileEntry.dir_idx, + fileEntry.mod_time, + fileEntry.length); + prologue.file_names.push_back(fileEntry); + } + break; + + default: + log->Printf( "0x%8.8x: DW_LNE_??? (%2.2x) - Skipping unknown upcode", op_offset, opcode); + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + offset += arg_size; + break; + } + } + break; + + // Standard Opcodes + case DW_LNS_copy: + log->Printf( "0x%8.8x: DW_LNS_copy", op_offset); + row.Dump (log); + break; + + case DW_LNS_advance_pc: + { + dw_uleb128_t addr_offset_n = debug_line_data.GetULEB128(&offset); + dw_uleb128_t addr_offset = addr_offset_n * prologue.min_inst_length; + log->Printf( "0x%8.8x: DW_LNS_advance_pc (0x%llx)", op_offset, addr_offset); + row.address += addr_offset; + } + break; + + case DW_LNS_advance_line: + { + dw_sleb128_t line_offset = debug_line_data.GetSLEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_advance_line (%i)", op_offset, line_offset); + row.line += line_offset; + } + break; + + case DW_LNS_set_file: + row.file = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_file (%u)", op_offset, row.file); + break; + + case DW_LNS_set_column: + row.column = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_column (%u)", op_offset, row.column); + break; + + case DW_LNS_negate_stmt: + row.is_stmt = !row.is_stmt; + log->Printf( "0x%8.8x: DW_LNS_negate_stmt", op_offset); + break; + + case DW_LNS_set_basic_block: + row.basic_block = true; + log->Printf( "0x%8.8x: DW_LNS_set_basic_block", op_offset); + break; + + case DW_LNS_const_add_pc: + { + uint8_t adjust_opcode = 255 - prologue.opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue.line_range) * prologue.min_inst_length; + log->Printf( "0x%8.8x: DW_LNS_const_add_pc (0x%8.8llx)", op_offset, addr_offset); + row.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + { + uint16_t pc_offset = debug_line_data.GetU16(&offset); + log->Printf( "0x%8.8x: DW_LNS_fixed_advance_pc (0x%4.4x)", op_offset, pc_offset); + row.address += pc_offset; + } + break; + + case DW_LNS_set_prologue_end: + row.prologue_end = true; + log->Printf( "0x%8.8x: DW_LNS_set_prologue_end", op_offset); + break; + + case DW_LNS_set_epilogue_begin: + row.epilogue_begin = true; + log->Printf( "0x%8.8x: DW_LNS_set_epilogue_begin", op_offset); + break; + + case DW_LNS_set_isa: + row.isa = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_isa (%u)", op_offset, row.isa); + break; + + // Special Opcodes + default: + if (opcode < prologue.opcode_base) + { + // We have an opcode that this parser doesn't know about, skip + // the number of ULEB128 numbers that is says to skip in the + // prologue's standard_opcode_lengths array + uint8_t n = prologue.standard_opcode_lengths[opcode-1]; + log->Printf( "0x%8.8x: Special : Unknown skipping %u ULEB128 values.", op_offset, n); + while (n > 0) + { + debug_line_data.GetULEB128(&offset); + --n; + } + } + else + { + uint8_t adjust_opcode = opcode - prologue.opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue.line_range) * prologue.min_inst_length; + int32_t line_offset = prologue.line_base + (adjust_opcode % prologue.line_range); + log->Printf("0x%8.8x: address += 0x%llx, line += %i\n", op_offset, (uint64_t)addr_offset, line_offset); + row.address += addr_offset; + row.line += line_offset; + row.Dump (log); + } + break; + } + } + return end_offset; + } + return DW_INVALID_OFFSET; +} + + + + +//---------------------------------------------------------------------- +// Parse +// +// Parse the entire line table contents calling callback each time a +// new prologue is parsed and every time a new row is to be added to +// the line table. +//---------------------------------------------------------------------- +void +DWARFDebugLine::Parse(const DataExtractor& debug_line_data, DWARFDebugLine::State::Callback callback, void* userData) +{ + uint32_t offset = 0; + if (debug_line_data.ValidOffset(offset)) + { + if (!ParseStatementTable(debug_line_data, &offset, callback, userData)) + ++offset; // Skip to next byte in .debug_line section + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::ParsePrologue +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParsePrologue(const DataExtractor& debug_line_data, dw_offset_t* offset_ptr, Prologue* prologue) +{ +// const uint32_t prologue_offset = *offset_ptr; + + //DEBUG_PRINTF("0x%8.8x: ParsePrologue()\n", *offset_ptr); + + prologue->Clear(); + uint32_t i; + const char * s; + prologue->total_length = debug_line_data.GetU32(offset_ptr); + prologue->version = debug_line_data.GetU16(offset_ptr); + if (prologue->version != 2) + return false; + + prologue->prologue_length = debug_line_data.GetU32(offset_ptr); + const dw_offset_t end_prologue_offset = prologue->prologue_length + *offset_ptr; + prologue->min_inst_length = debug_line_data.GetU8(offset_ptr); + prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr); + prologue->line_base = debug_line_data.GetU8(offset_ptr); + prologue->line_range = debug_line_data.GetU8(offset_ptr); + prologue->opcode_base = debug_line_data.GetU8(offset_ptr); + + prologue->standard_opcode_lengths.reserve(prologue->opcode_base-1); + + for (i=1; iopcode_base; ++i) + { + uint8_t op_len = debug_line_data.GetU8(offset_ptr); + prologue->standard_opcode_lengths.push_back(op_len); + } + + while (*offset_ptr < end_prologue_offset) + { + s = debug_line_data.GetCStr(offset_ptr); + if (s && s[0]) + prologue->include_directories.push_back(s); + else + break; + } + + while (*offset_ptr < end_prologue_offset) + { + const char* name = debug_line_data.GetCStr( offset_ptr ); + if (name && name[0]) + { + FileNameEntry fileEntry; + fileEntry.name = name; + fileEntry.dir_idx = debug_line_data.GetULEB128( offset_ptr ); + fileEntry.mod_time = debug_line_data.GetULEB128( offset_ptr ); + fileEntry.length = debug_line_data.GetULEB128( offset_ptr ); + prologue->file_names.push_back(fileEntry); + } + else + break; + } + + assert(*offset_ptr == end_prologue_offset); + return end_prologue_offset; +} + +bool +DWARFDebugLine::ParseSupportFiles(const DataExtractor& debug_line_data, const char *cu_comp_dir, dw_offset_t stmt_list, FileSpecList &support_files) +{ + uint32_t offset = stmt_list + 4; // Skip the total length + const char * s; + uint32_t version = debug_line_data.GetU16(&offset); + if (version != 2) + return false; + + const dw_offset_t end_prologue_offset = debug_line_data.GetU32(&offset) + offset; + // Skip instruction length, default is stmt, line base, line range and + // opcode base, and all opcode lengths + offset += 4; + const uint8_t opcode_base = debug_line_data.GetU8(&offset); + offset += opcode_base - 1; + std::vector include_directories; + include_directories.push_back(""); // Directory at index zero doesn't exist + while (offset < end_prologue_offset) + { + s = debug_line_data.GetCStr(&offset); + if (s && s[0]) + include_directories.push_back(s); + else + break; + } + std::string fullpath; + while (offset < end_prologue_offset) + { + const char* path = debug_line_data.GetCStr( &offset ); + if (path && path[0]) + { + uint32_t dir_idx = debug_line_data.GetULEB128( &offset ); + debug_line_data.Skip_LEB128(&offset); // Skip mod_time + debug_line_data.Skip_LEB128(&offset); // Skip length + + if (path[0] == '/') + { + // The path starts with a directory delimiter, so we are done. + fullpath = path; + } + else + { + if (dir_idx > 0 && dir_idx < include_directories.size()) + { + if (cu_comp_dir && include_directories[dir_idx][0] != '/') + { + fullpath = cu_comp_dir; + + if (*fullpath.rbegin() != '/') + fullpath += '/'; + fullpath += include_directories[dir_idx]; + + } + else + fullpath = include_directories[dir_idx]; + } + else if (cu_comp_dir && cu_comp_dir[0]) + { + fullpath = cu_comp_dir; + } + + if (!fullpath.empty()) + { + if (*fullpath.rbegin() != '/') + fullpath += '/'; + } + fullpath += path; + } + FileSpec file_spec(fullpath.c_str()); + support_files.Append(file_spec); + } + } + + assert(offset == end_prologue_offset); + return end_prologue_offset; +} + +//---------------------------------------------------------------------- +// ParseStatementTable +// +// Parse a single line table (prologue and all rows) and call the +// callback function once for the prologue (row in state will be zero) +// and each time a row is to be added to the line table. +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParseStatementTable +( + const DataExtractor& debug_line_data, + dw_offset_t* offset_ptr, + DWARFDebugLine::State::Callback callback, + void* userData +) +{ + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_LINE); + Prologue::shared_ptr prologue(new Prologue()); + + + const dw_offset_t debug_line_offset = *offset_ptr; + + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugLine::ParseStatementTable (.debug_line[0x%8.8x])", + debug_line_offset); + + if (!ParsePrologue(debug_line_data, offset_ptr, prologue.get())) + { + if (log) + log->Error ("failed to parse DWARF line table prologue"); + // Restore our offset and return false to indicate failure! + *offset_ptr = debug_line_offset; + return false; + } + + if (log) + prologue->Dump (log); + + const dw_offset_t end_offset = debug_line_offset + prologue->total_length + sizeof(prologue->total_length); + + assert(debug_line_data.ValidOffset(end_offset-1)); + + State state(prologue, log, callback, userData); + + while (*offset_ptr < end_offset) + { + //DEBUG_PRINTF("0x%8.8x: ", *offset_ptr); + uint8_t opcode = debug_line_data.GetU8(offset_ptr); + + if (opcode == 0) + { + // Extended Opcodes always start with a zero opcode followed by + // a uleb128 length so you can skip ones you don't know about + dw_offset_t ext_offset = *offset_ptr; + dw_uleb128_t len = debug_line_data.GetULEB128(offset_ptr); + dw_offset_t arg_size = len - (*offset_ptr - ext_offset); + + //DEBUG_PRINTF("Extended: <%2u> ", len); + uint8_t sub_opcode = debug_line_data.GetU8(offset_ptr); + switch (sub_opcode) + { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the + // state-machine registers. Then reset the registers to the initial + // values specified above. Every statement program sequence must end + // with a DW_LNE_end_sequence instruction which creates a row whose + // address is that of the byte after the last target machine instruction + // of the sequence. + state.end_sequence = true; + state.AppendRowToMatrix(*offset_ptr); + state.Reset(); + break; + + case DW_LNE_set_address: + // Takes a single relocatable address as an operand. The size of the + // operand is the size appropriate to hold an address on the target + // machine. Set the address register to the value given by the + // relocatable address. All of the other statement program opcodes + // that affect the address register add a delta to it. This instruction + // stores a relocatable value into it instead. + state.address = debug_line_data.GetAddress(offset_ptr); + break; + + case DW_LNE_define_file: + // Takes 4 arguments. The first is a null terminated string containing + // a source file name. The second is an unsigned LEB128 number representing + // the directory index of the directory in which the file was found. The + // third is an unsigned LEB128 number representing the time of last + // modification of the file. The fourth is an unsigned LEB128 number + // representing the length in bytes of the file. The time and length + // fields may contain LEB128(0) if the information is not available. + // + // The directory index represents an entry in the include_directories + // section of the statement program prologue. The index is LEB128(0) + // if the file was found in the current directory of the compilation, + // LEB128(1) if it was found in the first directory in the + // include_directories section, and so on. The directory index is + // ignored for file names that represent full path names. + // + // The files are numbered, starting at 1, in the order in which they + // appear; the names in the prologue come before names defined by + // the DW_LNE_define_file instruction. These numbers are used in the + // the file register of the state machine. + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(offset_ptr); + fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr); + fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr); + fileEntry.length = debug_line_data.GetULEB128(offset_ptr); + state.prologue->file_names.push_back(fileEntry); + } + break; + + default: + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + (*offset_ptr) += arg_size; + break; + } + } + else if (opcode < prologue->opcode_base) + { + switch (opcode) + { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the + // current values of the state-machine registers. Then set + // the basic_block register to false. + state.AppendRowToMatrix(*offset_ptr); + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the + // result to the address register of the state machine. + state.address += debug_line_data.GetULEB128(offset_ptr) * prologue->min_inst_length; + break; + + case DW_LNS_advance_line: + // Takes a single signed LEB128 operand and adds that value to + // the line register of the state machine. + state.line += debug_line_data.GetSLEB128(offset_ptr); + break; + + case DW_LNS_set_file: + // Takes a single unsigned LEB128 operand and stores it in the file + // register of the state machine. + state.file = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_set_column: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + state.column = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state + // machine to the logical negation of its current value. + state.is_stmt = !state.is_stmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the + // state machine to true + state.basic_block = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state + // machine the address increment value corresponding to special + // opcode 255. The motivation for DW_LNS_const_add_pc is this: + // when the statement program needs to advance the address by a + // small amount, it can use a single special opcode, which occupies + // a single byte. When it needs to advance the address by up to + // twice the range of the last special opcode, it can use + // DW_LNS_const_add_pc followed by a special opcode, for a total + // of two bytes. Only if it needs to advance the address by more + // than twice that range will it need to use both DW_LNS_advance_pc + // and a special opcode, requiring three or more bytes. + { + uint8_t adjust_opcode = 255 - prologue->opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + state.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + // Takes a single uhalf operand. Add to the address register of + // the state machine the value of the (unencoded) operand. This + // is the only extended opcode that takes an argument that is not + // a variable length number. The motivation for DW_LNS_fixed_advance_pc + // is this: existing assemblers cannot emit DW_LNS_advance_pc or + // special opcodes because they cannot encode LEB128 numbers or + // judge when the computation of a special opcode overflows and + // requires the use of DW_LNS_advance_pc. Such assemblers, however, + // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. + state.address += debug_line_data.GetU16(offset_ptr); + break; + + case DW_LNS_set_prologue_end: + // Takes no arguments. Set the prologue_end register of the + // state machine to true + state.prologue_end = true; + break; + + case DW_LNS_set_epilogue_begin: + // Takes no arguments. Set the basic_block register of the + // state machine to true + state.epilogue_begin = true; + break; + + case DW_LNS_set_isa: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + state.isa = debug_line_data.GetULEB128(offset_ptr); + break; + + default: + // Handle any unknown standard opcodes here. We know the lengths + // of such opcodes because they are specified in the prologue + // as a multiple of LEB128 operands for each opcode. + { + uint8_t i; + assert (opcode - 1 < prologue->standard_opcode_lengths.size()); + const uint8_t opcode_length = prologue->standard_opcode_lengths[opcode - 1]; + for (i=0; iopcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + int32_t line_offset = prologue->line_base + (adjust_opcode % prologue->line_range); + state.line += line_offset; + state.address += addr_offset; + state.AppendRowToMatrix(*offset_ptr); + } + } + + state.Finalize( *offset_ptr ); + + return end_offset; +} + + +//---------------------------------------------------------------------- +// ParseStatementTableCallback +//---------------------------------------------------------------------- +static void +ParseStatementTableCallback(dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + DWARFDebugLine::LineTable* line_table = (DWARFDebugLine::LineTable*)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // Just started parsing the line table, so lets keep a reference to + // the prologue using the supplied shared pointer + line_table->prologue = state.prologue; + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table, nothing to do for the cleanup + } + else + { + // We have a new row, lets append it + line_table->AppendRow(state); + } +} + +//---------------------------------------------------------------------- +// ParseStatementTable +// +// Parse a line table at offset and populate the LineTable class with +// the prologue and all rows. +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParseStatementTable(const DataExtractor& debug_line_data, uint32_t* offset_ptr, LineTable* line_table) +{ + return ParseStatementTable(debug_line_data, offset_ptr, ParseStatementTableCallback, line_table); +} + + +inline bool +DWARFDebugLine::Prologue::IsValid() const +{ + return SymbolFileDWARF::SupportedVersion(version); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::Prologue::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::Prologue::Dump(Log *log) +{ + uint32_t i; + + log->Printf( "Line table prologue:"); + log->Printf( " total_length: 0x%8.8x", total_length); + log->Printf( " version: %u", version); + log->Printf( "prologue_length: 0x%8.8x", prologue_length); + log->Printf( "min_inst_length: %u", min_inst_length); + log->Printf( "default_is_stmt: %u", default_is_stmt); + log->Printf( " line_base: %i", line_base); + log->Printf( " line_range: %u", line_range); + log->Printf( " opcode_base: %u", opcode_base); + + for (i=0; iPrintf( "standard_opcode_lengths[%s] = %u", DW_LNS_value_to_name(i+1), standard_opcode_lengths[i]); + } + + if (!include_directories.empty()) + { + for (i=0; iPrintf( "include_directories[%3u] = '%s'", i+1, include_directories[i].c_str()); + } + } + + if (!file_names.empty()) + { + log->PutCString (" Dir Mod Time File Len File Name"); + log->PutCString (" ---- ---------- ---------- ---------------------------"); + for (i=0; iPrintf ("file_names[%3u] %4u 0x%8.8x 0x%8.8x %s", + i+1, + fileEntry.dir_idx, + fileEntry.mod_time, + fileEntry.length, + fileEntry.name.c_str()); + } + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::ParsePrologue::Append +// +// Append the contents of the prologue to the binary stream buffer +//---------------------------------------------------------------------- +//void +//DWARFDebugLine::Prologue::Append(BinaryStreamBuf& buff) const +//{ +// uint32_t i; +// +// buff.Append32(total_length); +// buff.Append16(version); +// buff.Append32(prologue_length); +// buff.Append8(min_inst_length); +// buff.Append8(default_is_stmt); +// buff.Append8(line_base); +// buff.Append8(line_range); +// buff.Append8(opcode_base); +// +// for (i=0; iDump (log); + + if (!rows.empty()) + { + log->PutCString ("Address Line Column File ISA Flags"); + log->PutCString ("------------------ ------ ------ ------ --- -------------"); + Row::const_iterator pos = rows.begin(); + Row::const_iterator end = rows.end(); + while (pos != end) + { + (*pos).Dump (log); + ++pos; + } + } +} + + +void +DWARFDebugLine::LineTable::AppendRow(const DWARFDebugLine::Row& state) +{ + rows.push_back(state); +} + + + +//---------------------------------------------------------------------- +// Compare function for the binary search in DWARFDebugLine::LineTable::LookupAddress() +//---------------------------------------------------------------------- +static bool FindMatchingAddress (const DWARFDebugLine::Row& row1, const DWARFDebugLine::Row& row2) +{ + return row1.address < row2.address; +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::LineTable::LookupAddress +//---------------------------------------------------------------------- +uint32_t +DWARFDebugLine::LineTable::LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const +{ + uint32_t index = UINT_MAX; + if (!rows.empty()) + { + // Use the lower_bound algorithm to perform a binary search since we know + // that our line table data is ordered by address. + DWARFDebugLine::Row row; + row.address = address; + Row::const_iterator begin_pos = rows.begin(); + Row::const_iterator end_pos = rows.end(); + Row::const_iterator pos = lower_bound(begin_pos, end_pos, row, FindMatchingAddress); + if (pos == end_pos) + { + if (address < cu_high_pc) + return rows.size()-1; + } + else + { + // Rely on fact that we are using a std::vector and we can do + // pointer arithmetic to find the row index (which will be one less + // that what we found since it will find the first position after + // the current address) since std::vector iterators are just + // pointers to the container type. + index = pos - begin_pos; + if (pos->address > address) + { + if (index > 0) + --index; + else + index = UINT_MAX; + } + } + } + return index; // Failed to find address +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Row +//---------------------------------------------------------------------- +DWARFDebugLine::Row::Row(bool default_is_stmt) : + address(0), + line(1), + column(0), + file(1), + is_stmt(default_is_stmt), + basic_block(false), + end_sequence(false), + prologue_end(false), + epilogue_begin(false), + isa(0) +{ +} + +//---------------------------------------------------------------------- +// Called after a row is appended to the matrix +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::PostAppend() +{ + basic_block = false; + prologue_end = false; + epilogue_begin = false; +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Reset +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::Reset(bool default_is_stmt) +{ + address = 0; + line = 1; + column = 0; + file = 1; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + prologue_end = false; + epilogue_begin = false; + isa = 0; +} +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::Dump(Log *log) const +{ + log->Printf( "0x%16.16llx %6u %6u %6u %3u %s%s%s%s%s", + address, + line, + column, + file, + isa, + is_stmt ? " is_stmt" : "", + basic_block ? " basic_block" : "", + prologue_end ? " prologue_end" : "", + epilogue_begin ? " epilogue_begin" : "", + end_sequence ? " end_sequence" : ""); +} + +//---------------------------------------------------------------------- +// Compare function LineTable structures +//---------------------------------------------------------------------- +static bool AddressLessThan (const DWARFDebugLine::Row& a, const DWARFDebugLine::Row& b) +{ + return a.address < b.address; +} + + + +// Insert a row at the correct address if the addresses can be out of +// order which can only happen when we are linking a line table that +// may have had it's contents rearranged. +void +DWARFDebugLine::Row::Insert(Row::collection& state_coll, const Row& state) +{ + // If we don't have anything yet, or if the address of the last state in our + // line table is less than the current one, just append the current state + if (state_coll.empty() || AddressLessThan(state_coll.back(), state)) + { + state_coll.push_back(state); + } + else + { + // Do a binary search for the correct entry + pair range(equal_range(state_coll.begin(), state_coll.end(), state, AddressLessThan)); + + // If the addresses are equal, we can safely replace the previous entry + // with the current one if the one it is replacing is an end_sequence entry. + // We currently always place an extra end sequence when ever we exit a valid + // address range for a function in case the functions get rearranged by + // optmimizations or by order specifications. These extra end sequences will + // disappear by getting replaced with valid consecutive entries within a + // compile unit if there are no gaps. + if (range.first == range.second) + { + state_coll.insert(range.first, state); + } + else + { + if ((distance(range.first, range.second) == 1) && range.first->end_sequence == true) + { + *range.first = state; + } + else + { + state_coll.insert(range.second, state); + } + } + } +} + +void +DWARFDebugLine::Row::Dump(Log *log, const Row::collection& state_coll) +{ + std::for_each (state_coll.begin(), state_coll.end(), bind2nd(std::mem_fun_ref(&Row::Dump),log)); +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::State +//---------------------------------------------------------------------- +DWARFDebugLine::State::State(Prologue::shared_ptr& p, Log *l, DWARFDebugLine::State::Callback cb, void* userData) : + Row (p->default_is_stmt), + prologue (p), + log (l), + callback (cb), + callbackUserData (userData), + row (StartParsingLineTable) +{ + // Call the callback with the initial row state of zero for the prologue + if (callback) + callback(0, *this, callbackUserData); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::Reset +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::Reset() +{ + Row::Reset(prologue->default_is_stmt); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::AppendRowToMatrix +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::AppendRowToMatrix(dw_offset_t offset) +{ + // Each time we are to add an entry into the line table matrix + // call the callback funtion so that someone can do something with + // the current state of the state machine (like build a line table + // or dump the line table!) + if (log) + { + if (row == 0) + { + log->PutCString ("Address Line Column File ISA Flags"); + log->PutCString ("------------------ ------ ------ ------ --- -------------"); + } + Dump (log); + } + + ++row; // Increase the row number before we call our callback for a real row + if (callback) + callback(offset, *this, callbackUserData); + PostAppend(); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::Finalize +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::Finalize(dw_offset_t offset) +{ + // Call the callback with a special row state when we are done parsing a + // line table + row = DoneParsingLineTable; + if (callback) + callback(offset, *this, callbackUserData); +} + +//void +//DWARFDebugLine::AppendLineTableData +//( +// const DWARFDebugLine::Prologue* prologue, +// const DWARFDebugLine::Row::collection& state_coll, +// const uint32_t addr_size, +// BinaryStreamBuf &debug_line_data +//) +//{ +// if (state_coll.empty()) +// { +// // We have no entries, just make an empty line table +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// } +// else +// { +// DWARFDebugLine::Row::const_iterator pos; +// Row::const_iterator end = state_coll.end(); +// bool default_is_stmt = prologue->default_is_stmt; +// const DWARFDebugLine::Row reset_state(default_is_stmt); +// const DWARFDebugLine::Row* prev_state = &reset_state; +// const int32_t max_line_increment_for_special_opcode = prologue->MaxLineIncrementForSpecialOpcode(); +// for (pos = state_coll.begin(); pos != end; ++pos) +// { +// const DWARFDebugLine::Row& curr_state = *pos; +// int32_t line_increment = 0; +// dw_addr_t addr_offset = curr_state.address - prev_state->address; +// dw_addr_t addr_advance = (addr_offset) / prologue->min_inst_length; +// line_increment = (int32_t)(curr_state.line - prev_state->line); +// +// // If our previous state was the reset state, then let's emit the +// // address to keep GDB's DWARF parser happy. If we don't start each +// // sequence with a DW_LNE_set_address opcode, the line table won't +// // get slid properly in GDB. +// +// if (prev_state == &reset_state) +// { +// debug_line_data.Append8(0); // Extended opcode +// debug_line_data.Append32_as_ULEB128(addr_size + 1); // Length of opcode bytes +// debug_line_data.Append8(DW_LNE_set_address); +// debug_line_data.AppendMax64(curr_state.address, addr_size); +// addr_advance = 0; +// } +// +// if (prev_state->file != curr_state.file) +// { +// debug_line_data.Append8(DW_LNS_set_file); +// debug_line_data.Append32_as_ULEB128(curr_state.file); +// } +// +// if (prev_state->column != curr_state.column) +// { +// debug_line_data.Append8(DW_LNS_set_column); +// debug_line_data.Append32_as_ULEB128(curr_state.column); +// } +// +// // Don't do anything fancy if we are at the end of a sequence +// // as we don't want to push any extra rows since the DW_LNE_end_sequence +// // will push a row itself! +// if (curr_state.end_sequence) +// { +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push the end sequence on! +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// +// prev_state = &reset_state; +// } +// else +// { +// if (line_increment || addr_advance) +// { +// if (line_increment > max_line_increment_for_special_opcode) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// line_increment = 0; +// } +// +// uint32_t special_opcode = (line_increment >= prologue->line_base) ? ((line_increment - prologue->line_base) + (prologue->line_range * addr_advance) + prologue->opcode_base) : 256; +// if (special_opcode > 255) +// { +// // Both the address and line won't fit in one special opcode +// // check to see if just the line advance will? +// uint32_t special_opcode_line = ((line_increment >= prologue->line_base) && (line_increment != 0)) ? +// ((line_increment - prologue->line_base) + prologue->opcode_base) : 256; +// +// +// if (special_opcode_line > 255) +// { +// // Nope, the line advance won't fit by itself, check the address increment by itself +// uint32_t special_opcode_addr = addr_advance ? +// ((0 - prologue->line_base) + (prologue->line_range * addr_advance) + prologue->opcode_base) : 256; +// +// if (special_opcode_addr > 255) +// { +// // Neither the address nor the line will fit in a +// // special opcode, we must manually enter both then +// // do a DW_LNS_copy to push a row (special opcode +// // automatically imply a new row is pushed) +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push a row onto the line table manually +// debug_line_data.Append8(DW_LNS_copy); +// +// } +// else +// { +// // The address increment alone will fit into a special opcode +// // so modify our line change, then issue a special opcode +// // for the address increment and it will push a row into the +// // line table +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_addr); +// } +// } +// else +// { +// // The line change alone will fit into a special opcode +// // so modify our address increment first, then issue a +// // special opcode for the line change and it will push +// // a row into the line table +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_line); +// } +// } +// else +// { +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode); +// } +// } +// prev_state = &curr_state; +// } +// } +// } +//} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h new file mode 100644 index 000000000000..57b2f1596614 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h @@ -0,0 +1,225 @@ +//===-- DWARFDebugLine.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugLine_h_ +#define liblldb_DWARFDebugLine_h_ + +#include +#include +#include + +#include "lldb/lldb-private.h" + +#include "DWARFDefines.h" + +class SymbolFileDWARF; +class DWARFDebugInfoEntry; + +//---------------------------------------------------------------------- +// DWARFDebugLine +//---------------------------------------------------------------------- +class DWARFDebugLine +{ +public: + //------------------------------------------------------------------ + // FileNameEntry + //------------------------------------------------------------------ + struct FileNameEntry + { + FileNameEntry() : + name(), + dir_idx(0), + mod_time(0), + length(0) + { + } + + std::string name; + dw_sleb128_t dir_idx; + dw_sleb128_t mod_time; + dw_sleb128_t length; + + }; + + //------------------------------------------------------------------ + // Prologue + //------------------------------------------------------------------ + struct Prologue + { + + Prologue() : + total_length(0), + version(0), + prologue_length(0), + min_inst_length(0), + default_is_stmt(0), + line_base(0), + line_range(0), + opcode_base(0), + standard_opcode_lengths(), + include_directories(), + file_names() + { + } + + typedef lldb::SharedPtr::Type shared_ptr; + + uint32_t total_length; // The size in bytes of the statement information for this compilation unit (not including the total_length field itself). + uint16_t version; // Version identifier for the statement information format. + uint32_t prologue_length;// The number of bytes following the prologue_length field to the beginning of the first byte of the statement program itself. + uint8_t min_inst_length;// The size in bytes of the smallest target machine instruction. Statement program opcodes that alter the address register first multiply their operands by this value. + uint8_t default_is_stmt;// The initial value of theis_stmtregister. + int8_t line_base; // This parameter affects the meaning of the special opcodes. See below. + uint8_t line_range; // This parameter affects the meaning of the special opcodes. See below. + uint8_t opcode_base; // The number assigned to the first special opcode. + std::vector standard_opcode_lengths; + std::vector include_directories; + std::vector file_names; + + // Length of the prologue in bytes + uint32_t Length() const { return prologue_length + sizeof(total_length) + sizeof(version) + sizeof(prologue_length); } + // Length of the line table data in bytes (not including the prologue) + uint32_t StatementTableLength() const { return total_length + sizeof(total_length) - Length(); } + int32_t MaxLineIncrementForSpecialOpcode() const { return line_base + (int8_t)line_range - 1; }; + bool IsValid() const; +// void Append(BinaryStreamBuf& buff) const; + void Dump (lldb_private::Log *log); + void Clear() + { + total_length = version = prologue_length = min_inst_length = line_base = line_range = opcode_base = 0; + line_base = 0; + standard_opcode_lengths.clear(); + include_directories.clear(); + file_names.clear(); + } + bool GetFile(uint32_t file_idx, std::string& file, std::string& dir) const; + + }; + + // Standard .debug_line state machine structure + struct Row + { + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + Row(bool default_is_stmt = false); + virtual ~Row() {} + void PostAppend (); + void Reset(bool default_is_stmt); + void Dump(lldb_private::Log *log) const; + static void Insert(Row::collection& state_coll, const Row& state); + static void Dump(lldb_private::Log *log, const Row::collection& state_coll); + + dw_addr_t address; // The program-counter value corresponding to a machine instruction generated by the compiler. + uint32_t line; // An unsigned integer indicating a source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. + uint16_t column; // An unsigned integer indicating a column number within a source line. Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the ‘‘left edge’’ of the line. + uint16_t file; // An unsigned integer indicating the identity of the source file corresponding to a machine instruction. + uint8_t is_stmt:1, // A boolean indicating that the current instruction is the beginning of a statement. + basic_block:1, // A boolean indicating that the current instruction is the beginning of a basic block. + end_sequence:1, // A boolean indicating that the current address is that of the first byte after the end of a sequence of target machine instructions. + prologue_end:1, // A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + epilogue_begin:1;// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + uint32_t isa; // An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. + }; + + + //------------------------------------------------------------------ + // LineTable + //------------------------------------------------------------------ + struct LineTable + { + typedef lldb::SharedPtr::Type shared_ptr; + + LineTable() : + prologue(), + rows() + { + } + + void AppendRow(const DWARFDebugLine::Row& state); + void Clear() + { + prologue.reset(); + rows.clear(); + } + + uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const; + void Dump(lldb_private::Log *log) const; + + Prologue::shared_ptr prologue; + Row::collection rows; + }; + + //------------------------------------------------------------------ + // State + //------------------------------------------------------------------ + struct State : public Row + { + typedef void (*Callback)(dw_offset_t offset, const State& state, void* userData); + + // Special row codes used when calling the callback + enum + { + StartParsingLineTable = 0, + DoneParsingLineTable = -1 + }; + + State (Prologue::shared_ptr& prologue_sp, + lldb_private::Log *log, + Callback callback, + void* userData); + + void + AppendRowToMatrix (dw_offset_t offset); + + void + Finalize (dw_offset_t offset); + + void + Reset (); + + Prologue::shared_ptr prologue; + lldb_private::Log *log; + Callback callback; // Callback funcation that gets called each time an entry it to be added to the matrix + void* callbackUserData; + int row; // The row number that starts at zero for the prologue, and increases for each row added to the matrix + private: + DISALLOW_COPY_AND_ASSIGN (State); + }; + + static bool DumpOpcodes(lldb_private::Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t line_offset = DW_INVALID_OFFSET, uint32_t dump_flags = 0); // If line_offset is invalid, dump everything + static bool DumpLineTableRows(lldb_private::Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t line_offset = DW_INVALID_OFFSET); // If line_offset is invalid, dump everything + static bool ParseSupportFiles(const lldb_private::DataExtractor& debug_line_data, const char *cu_comp_dir, dw_offset_t stmt_list, lldb_private::FileSpecList &support_files); + static bool ParsePrologue(const lldb_private::DataExtractor& debug_line_data, dw_offset_t* offset_ptr, Prologue* prologue); + static bool ParseStatementTable(const lldb_private::DataExtractor& debug_line_data, dw_offset_t* offset_ptr, State::Callback callback, void* userData); + static dw_offset_t DumpStatementTable(lldb_private::Log *log, const lldb_private::DataExtractor& debug_line_data, const dw_offset_t line_offset); + static dw_offset_t DumpStatementOpcodes(lldb_private::Log *log, const lldb_private::DataExtractor& debug_line_data, const dw_offset_t line_offset, uint32_t flags); + static bool ParseStatementTable(const lldb_private::DataExtractor& debug_line_data, uint32_t* offset_ptr, LineTable* line_table); + static void Parse(const lldb_private::DataExtractor& debug_line_data, DWARFDebugLine::State::Callback callback, void* userData); +// static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue, const DWARFDebugLine::Row::collection& state_coll, const uint32_t addr_size, BinaryStreamBuf &debug_line_data); + + DWARFDebugLine() : + m_lineTableMap() + { + } + + void Parse(const lldb_private::DataExtractor& debug_line_data); + void ParseIfNeeded(const lldb_private::DataExtractor& debug_line_data); + LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const; + +protected: + typedef std::map LineTableMap; + typedef LineTableMap::iterator LineTableIter; + typedef LineTableMap::const_iterator LineTableConstIter; + + LineTableMap m_lineTableMap; +}; + +#endif // liblldb_DWARFDebugLine_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp new file mode 100644 index 000000000000..0501da8fe409 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp @@ -0,0 +1,48 @@ +//===-- DWARFDebugMacinfo.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugMacinfo.h" + +#include "DWARFDebugMacinfoEntry.h" +#include "SymbolFileDWARF.h" + +#include "lldb/Core/Stream.h" + +using namespace lldb_private; +using namespace std; + +DWARFDebugMacinfo::DWARFDebugMacinfo() +{ +} + +DWARFDebugMacinfo::~DWARFDebugMacinfo() +{ +} + +void +DWARFDebugMacinfo::Dump(Stream *s, const DataExtractor& macinfo_data, dw_offset_t offset) +{ + DWARFDebugMacinfoEntry maninfo_entry; + if (macinfo_data.GetByteSize() == 0) + { + s->PutCString("< EMPTY >\n"); + return; + } + if (offset == DW_INVALID_OFFSET) + { + offset = 0; + while (maninfo_entry.Extract(macinfo_data, &offset)) + maninfo_entry.Dump(s); + } + else + { + if (maninfo_entry.Extract(macinfo_data, &offset)) + maninfo_entry.Dump(s); + } +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h new file mode 100644 index 000000000000..e420a5be84e0 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h @@ -0,0 +1,29 @@ +//===-- DWARFDebugMacinfo.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugLine_h_ +#define SymbolFileDWARF_DWARFDebugLine_h_ + +#include "SymbolFileDWARF.h" + +class DWARFDebugMacinfo +{ +public: + DWARFDebugMacinfo(); + + ~DWARFDebugMacinfo(); + + static void + Dump (lldb_private::Stream *s, + const lldb_private::DataExtractor& macinfo_data, + dw_offset_t offset = DW_INVALID_OFFSET); +}; + + +#endif // SymbolFileDWARF_DWARFDebugLine_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp new file mode 100644 index 000000000000..c07cec45c2a7 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp @@ -0,0 +1,132 @@ +//===-- DWARFDebugMacinfoEntry.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugMacinfoEntry.h" + +#include "lldb/Core/Stream.h" + +using namespace lldb_private; +using namespace std; + +DWARFDebugMacinfoEntry::DWARFDebugMacinfoEntry() : + m_type_code(0), + m_line(0), + m_op2() +{ + m_op2.cstr = NULL; +} + +DWARFDebugMacinfoEntry::~DWARFDebugMacinfoEntry() +{ +} + +const char* +DWARFDebugMacinfoEntry::GetCString() const +{ + switch (m_type_code) + { + case 0: + case DW_MACINFO_start_file: + case DW_MACINFO_end_file: + return NULL; + default: + break; + } + return m_op2.cstr; +} + + + +void +DWARFDebugMacinfoEntry::Dump(Stream *s) const +{ + if (m_type_code) + { + s->PutCString(DW_MACINFO_value_to_name(m_type_code)); + + switch (m_type_code) + { + case DW_MACINFO_define: + s->Printf(" line:%u #define %s\n", (uint32_t)m_line, m_op2.cstr); + break; + + case DW_MACINFO_undef: + s->Printf(" line:%u #undef %s\n", (uint32_t)m_line, m_op2.cstr); + break; + + default: + s->Printf(" line:%u str: '%s'\n", (uint32_t)m_line, m_op2.cstr); + break; + + case DW_MACINFO_start_file: + s->Printf(" line:%u file index: '%s'\n", (uint32_t)m_line, (uint32_t)m_op2.file_idx); + break; + + case DW_MACINFO_end_file: + break; + } + } + else + { + s->PutCString(" END\n"); + } +} + + +bool +DWARFDebugMacinfoEntry::Extract(const DataExtractor& mac_info_data, dw_offset_t* offset_ptr) +{ + if (mac_info_data.ValidOffset(*offset_ptr)) + { + m_type_code = mac_info_data.GetU8(offset_ptr); + + switch (m_type_code) + { + + case DW_MACINFO_define: + case DW_MACINFO_undef: + // 2 operands: + // Arg 1: operand encodes the line number of the source line on which + // the relevant defining or undefining pre-processor directives + // appeared. + m_line = mac_info_data.GetULEB128(offset_ptr); + // Arg 2: define string + m_op2.cstr = mac_info_data.GetCStr(offset_ptr); + break; + + case DW_MACINFO_start_file: + // 2 operands: + // Op 1: line number of the source line on which the inclusion + // pre-processor directive occurred. + m_line = mac_info_data.GetULEB128(offset_ptr); + // Op 2: a source file name index to a file number in the statement + // information table for the relevant compilation unit. + m_op2.file_idx = mac_info_data.GetULEB128(offset_ptr); + break; + + case 0: // End of list + case DW_MACINFO_end_file: + // No operands + m_line = DW_INVALID_OFFSET; + m_op2.cstr = NULL; + break; + default: + // Vendor specific entries always have a ULEB128 and a string + m_line = mac_info_data.GetULEB128(offset_ptr); + m_op2.cstr = mac_info_data.GetCStr(offset_ptr); + break; + } + return true; + } + else + m_type_code = 0; + + return false; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h new file mode 100644 index 000000000000..85dd62548337 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h @@ -0,0 +1,57 @@ +//===-- DWARFDebugMacinfoEntry.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugMacinfoEntry_h_ +#define SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ + +#include "SymbolFileDWARF.h" + +class DWARFDebugMacinfoEntry +{ +public: + DWARFDebugMacinfoEntry(); + + ~DWARFDebugMacinfoEntry(); + + uint8_t + TypeCode() const + { + return m_type_code; + } + + uint8_t + GetLineNumber() const + { + return m_line; + } + + void + Dump(lldb_private::Stream *s) const; + + const char* + GetCString() const; + + bool + Extract(const lldb_private::DataExtractor& mac_info_data, + dw_offset_t* offset_ptr); + +protected: + +private: + uint8_t m_type_code; + dw_uleb128_t m_line; + union + { + dw_uleb128_t file_idx; + const char* cstr; + } m_op2; +}; + + +#endif // SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp new file mode 100644 index 000000000000..93ecaed72c69 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp @@ -0,0 +1,297 @@ +//===-- DWARFDebugPubnames.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugPubnames.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "DWARFDebugInfo.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFCompileUnit.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + + +using namespace lldb_private; + +DWARFDebugPubnames::DWARFDebugPubnames() : + m_sets() +{ +} + +bool +DWARFDebugPubnames::Extract(const DataExtractor& data) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugPubnames::Extract (byte_size = %zu)", + data.GetByteSize()); + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_PUBNAMES); + if (log) + log->Printf("DWARFDebugPubnames::Extract (byte_size = %zu)", data.GetByteSize()); + + if (data.ValidOffset(0)) + { + uint32_t offset = 0; + + DWARFDebugPubnamesSet set; + while (data.ValidOffset(offset)) + { + if (set.Extract(data, &offset)) + { + m_sets.push_back(set); + offset = set.GetOffsetOfNextEntry(); + } + else + break; + } + if (log) + Dump (log); + return true; + } + return false; +} + + +bool +DWARFDebugPubnames::GeneratePubnames(SymbolFileDWARF* dwarf2Data) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugPubnames::GeneratePubnames (data = %p)", + dwarf2Data); + + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_PUBNAMES); + if (log) + log->Printf("DWARFDebugPubnames::GeneratePubnames (data = %p)", dwarf2Data); + + m_sets.clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + + const DataExtractor* debug_str = &dwarf2Data->get_debug_str_data(); + + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + + bool clear_dies = cu->ExtractDIEsIfNeeded (false) > 1; + + DWARFDIECollection dies; + const size_t die_count = cu->AppendDIEsWithTag (DW_TAG_subprogram, dies) + + cu->AppendDIEsWithTag (DW_TAG_variable, dies); + + dw_offset_t cu_offset = cu->GetOffset(); + DWARFDebugPubnamesSet pubnames_set(DW_INVALID_OFFSET, cu_offset, cu->GetNextCompileUnitOffset() - cu_offset); + + size_t die_idx; + for (die_idx = 0; die_idx < die_count; ++die_idx) + { + const DWARFDebugInfoEntry *die = dies.GetDIEPtrAtIndex(die_idx); + DWARFDebugInfoEntry::Attributes attributes; + const char *name = NULL; + const char *mangled = NULL; + bool add_die = false; + bool is_variable = false; + const size_t num_attributes = die->GetAttributes(dwarf2Data, cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + + dw_tag_t tag = die->Tag(); + + is_variable = tag == DW_TAG_variable; + + for (i=0; iGetParent(); + while ( parent_die != NULL ) + { + switch (parent_die->Tag()) + { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We could theoretically + // add these if we wanted to by introspecting into the DW_AT_location and seeing + // if the location describes a hard coded address, but we dont want the performance + // penalty of that right now. + add_die = false; +// if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) +// { +// // If we have valid block data, then we have location expression bytes +// // that are fixed (not a location list). +// const uint8_t *block_data = form_value.BlockData(); +// if (block_data) +// { +// uint32_t block_length = form_value.Unsigned(); +// if (block_length == 1 + attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) +// { +// if (block_data[0] == DW_OP_addr) +// add_die = true; +// } +// } +// } + parent_die = NULL; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + add_die = true; + parent_die = NULL; // Terminate the while loop. + break; + + default: + parent_die = parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + } + } + } + + if (add_die && (name || mangled)) + { + if (is_variable) + cu->AddGlobal(die); + pubnames_set.AddDescriptor(die->GetOffset() - cu_offset, mangled ? mangled : name); + } + } + + if (pubnames_set.NumDescriptors() > 0) + { + m_sets.push_back(pubnames_set); + } + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + cu->ClearDIEs (true); + } + } + if (m_sets.empty()) + return false; + if (log) + Dump (log); + return true; +} + +bool +DWARFDebugPubnames::GeneratePubBaseTypes(SymbolFileDWARF* dwarf2Data) +{ + m_sets.clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + DWARFDIECollection dies; + const size_t die_count = cu->AppendDIEsWithTag (DW_TAG_base_type, dies); + dw_offset_t cu_offset = cu->GetOffset(); + DWARFDebugPubnamesSet pubnames_set(DW_INVALID_OFFSET, cu_offset, cu->GetNextCompileUnitOffset() - cu_offset); + + size_t die_idx; + for (die_idx = 0; die_idx < die_count; ++die_idx) + { + const DWARFDebugInfoEntry *die = dies.GetDIEPtrAtIndex(die_idx); + const char *name = die->GetAttributeValueAsString(dwarf2Data, cu, DW_AT_name, NULL); + + if (name) + { + pubnames_set.AddDescriptor(die->GetOffset() - cu_offset, name); + } + } + + if (pubnames_set.NumDescriptors() > 0) + { + m_sets.push_back(pubnames_set); + } + } + } + return !m_sets.empty(); +} + +void +DWARFDebugPubnames::Dump(Log *s) const +{ + if (m_sets.empty()) + s->PutCString("< EMPTY >\n"); + else + { + const_iterator pos; + const_iterator end = m_sets.end(); + + for (pos = m_sets.begin(); pos != end; ++pos) + (*pos).Dump(s); + } +} + +bool +DWARFDebugPubnames::Find(const char* name, bool ignore_case, std::vector& die_offsets) const +{ + const_iterator pos; + const_iterator end = m_sets.end(); + + die_offsets.clear(); + + for (pos = m_sets.begin(); pos != end; ++pos) + { + (*pos).Find(name, ignore_case, die_offsets); + } + + return !die_offsets.empty(); +} + +bool +DWARFDebugPubnames::Find(const RegularExpression& regex, std::vector& die_offsets) const +{ + const_iterator pos; + const_iterator end = m_sets.end(); + + die_offsets.clear(); + + for (pos = m_sets.begin(); pos != end; ++pos) + { + (*pos).Find(regex, die_offsets); + } + + return !die_offsets.empty(); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h new file mode 100644 index 000000000000..7d09bf3b55a9 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h @@ -0,0 +1,38 @@ +//===-- DWARFDebugPubnames.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugPubnames_h_ +#define SymbolFileDWARF_DWARFDebugPubnames_h_ + +#include "SymbolFileDWARF.h" + +#include + +#include "DWARFDebugPubnamesSet.h" + +class DWARFDebugPubnames +{ +public: + DWARFDebugPubnames(); + bool Extract(const lldb_private::DataExtractor& data); + bool GeneratePubnames(SymbolFileDWARF* dwarf2Data); + bool GeneratePubBaseTypes(SymbolFileDWARF* dwarf2Data); + + void Dump(lldb_private::Log *s) const; + bool Find(const char* name, bool ignore_case, std::vector& die_offset_coll) const; + bool Find(const lldb_private::RegularExpression& regex, std::vector& die_offsets) const; +protected: + typedef std::list collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_sets; +}; + +#endif // SymbolFileDWARF_DWARFDebugPubnames_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp new file mode 100644 index 000000000000..0421ced55d41 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp @@ -0,0 +1,166 @@ +//===-- DWARFDebugPubnamesSet.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugPubnamesSet.h" + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Log.h" + +#include "SymbolFileDWARF.h" + +using namespace lldb_private; + +DWARFDebugPubnamesSet::DWARFDebugPubnamesSet() : + m_offset(DW_INVALID_OFFSET), + m_header(), + m_descriptors(), + m_name_to_descriptor_index() +{ +} + +DWARFDebugPubnamesSet::DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset, dw_offset_t cu_die_offset, dw_offset_t cu_die_length) : + m_offset(debug_aranges_offset), + m_header(), + m_descriptors(), + m_name_to_descriptor_index() +{ + m_header.length = 10; // set the length to only include the header right for now + m_header.version = 2; // The DWARF version number + m_header.die_offset = cu_die_offset;// compile unit .debug_info offset + m_header.die_length = cu_die_length;// compile unit .debug_info length +} + +void +DWARFDebugPubnamesSet::AddDescriptor(dw_offset_t cu_rel_offset, const char* name) +{ + if (name && name[0]) + { + // Adjust our header length + m_header.length += strlen(name) + 1 + sizeof(dw_offset_t); + Descriptor pubnameDesc(cu_rel_offset, name); + m_descriptors.push_back(pubnameDesc); + } +} + +void +DWARFDebugPubnamesSet::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_header.length = 10; + m_header.version = 2; + m_header.die_offset = DW_INVALID_OFFSET; + m_header.die_length = 0; + m_descriptors.clear(); +} + + +//---------------------------------------------------------------------- +// InitNameIndexes +//---------------------------------------------------------------------- +void +DWARFDebugPubnamesSet::InitNameIndexes() const +{ + // Create the name index vector to be able to quickly search by name + const size_t count = m_descriptors.size(); + for (uint32_t idx = 0; idx < count; ++idx) + { + const char* name = m_descriptors[idx].name.c_str(); + if (name && name[0]) + m_name_to_descriptor_index.insert(cstr_to_index_mmap::value_type(name, idx)); + } +} + + +bool +DWARFDebugPubnamesSet::Extract(const DataExtractor& data, uint32_t* offset_ptr) +{ + if (data.ValidOffset(*offset_ptr)) + { + m_descriptors.clear(); + m_offset = *offset_ptr; + m_header.length = data.GetU32(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.die_offset = data.GetU32(offset_ptr); + m_header.die_length = data.GetU32(offset_ptr); + + Descriptor pubnameDesc; + while (data.ValidOffset(*offset_ptr)) + { + pubnameDesc.offset = data.GetU32(offset_ptr); + + if (pubnameDesc.offset) + { + const char* name = data.GetCStr(offset_ptr); + if (name && name[0]) + { + pubnameDesc.name = name; + m_descriptors.push_back(pubnameDesc); + } + } + else + break; // We are done if we get a zero 4 byte offset + } + + return !m_descriptors.empty(); + } + return false; +} + +dw_offset_t +DWARFDebugPubnamesSet::GetOffsetOfNextEntry() const +{ + return m_offset + m_header.length + 4; +} + +void +DWARFDebugPubnamesSet::Dump(Log *log) const +{ + log->Printf("Pubnames Header: length = 0x%8.8x, version = 0x%4.4x, die_offset = 0x%8.8x, die_length = 0x%8.8x", + m_header.length, + m_header.version, + m_header.die_offset, + m_header.die_length); + + bool verbose = log->GetVerbose(); + + DescriptorConstIter pos; + DescriptorConstIter end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; ++pos) + { + if (verbose) + log->Printf("0x%8.8x + 0x%8.8x = 0x%8.8x: %s", pos->offset, m_header.die_offset, pos->offset + m_header.die_offset, pos->name.c_str()); + else + log->Printf("0x%8.8x: %s", pos->offset + m_header.die_offset, pos->name.c_str()); + } +} + + +void +DWARFDebugPubnamesSet::Find(const char* name, bool ignore_case, std::vector& die_offset_coll) const +{ + if (!m_descriptors.empty() && m_name_to_descriptor_index.empty()) + InitNameIndexes(); + + std::pair range(m_name_to_descriptor_index.equal_range(name)); + for (cstr_to_index_mmap::const_iterator pos = range.first; pos != range.second; ++pos) + die_offset_coll.push_back(m_header.die_offset + m_descriptors[(*pos).second].offset); +} + +void +DWARFDebugPubnamesSet::Find(const RegularExpression& regex, std::vector& die_offset_coll) const +{ + DescriptorConstIter pos; + DescriptorConstIter end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; ++pos) + { + if ( regex.Execute(pos->name.c_str()) ) + die_offset_coll.push_back(m_header.die_offset + pos->offset); + } +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h new file mode 100644 index 000000000000..0597e368e1ff --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h @@ -0,0 +1,91 @@ +//===-- DWARFDebugPubnamesSet.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugPubnamesSet_h_ +#define SymbolFileDWARF_DWARFDebugPubnamesSet_h_ + +#include "SymbolFileDWARF.h" +#include +#include +#include + +class DWARFDebugPubnamesSet +{ +public: + struct Header + { + uint32_t length; // length of the set of entries for this compilation unit, not including the length field itself + uint16_t version; // The DWARF version number + uint32_t die_offset; // compile unit .debug_info offset + uint32_t die_length; // compile unit .debug_info length + Header() : + length(10), + version(2), + die_offset(DW_INVALID_OFFSET), + die_length(0) + { + } + }; + + struct Descriptor + { + Descriptor() : + offset(), + name() + { + } + + Descriptor(dw_offset_t the_offset, const char *the_name) : + offset(the_offset), + name(the_name ? the_name : "") + { + } + + dw_offset_t offset; + std::string name; + }; + + DWARFDebugPubnamesSet(); + DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset, dw_offset_t cu_die_offset, dw_offset_t die_length); + dw_offset_t GetOffset() const { return m_offset; } + void SetOffset(dw_offset_t offset) { m_offset = offset; } + DWARFDebugPubnamesSet::Header& GetHeader() { return m_header; } + const DWARFDebugPubnamesSet::Header& GetHeader() const { return m_header; } + const DWARFDebugPubnamesSet::Descriptor* GetDescriptor(uint32_t i) const + { + if (i < m_descriptors.size()) + return &m_descriptors[i]; + return NULL; + } + uint32_t NumDescriptors() const { return m_descriptors.size(); } + void AddDescriptor(dw_offset_t cu_rel_offset, const char* name); + void Clear(); + bool Extract(const lldb_private::DataExtractor& debug_pubnames_data, uint32_t* offset_ptr); + void Dump(lldb_private::Log *s) const; + void InitNameIndexes() const; + void Find(const char* name, bool ignore_case, std::vector& die_offset_coll) const; + void Find(const lldb_private::RegularExpression& regex, std::vector& die_offsets) const; + dw_offset_t GetOffsetOfNextEntry() const; + + + +protected: + typedef std::vector DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + + dw_offset_t m_offset; + Header m_header; + typedef __gnu_cxx::hash_multimap, CStringEqualBinaryPredicate> cstr_to_index_mmap; + DescriptorColl m_descriptors; + mutable cstr_to_index_mmap m_name_to_descriptor_index; +}; + +#endif // SymbolFileDWARF_DWARFDebugPubnamesSet_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp new file mode 100644 index 000000000000..62da22855f7d --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp @@ -0,0 +1,275 @@ +//===-- DWARFDebugRanges.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugRanges.h" +#include "SymbolFileDWARF.h" +#include "lldb/Core/Stream.h" +#include + +using namespace lldb_private; +using namespace std; + +DWARFDebugRanges::DWARFDebugRanges() : + m_range_map() +{ +} + +DWARFDebugRanges::~DWARFDebugRanges() +{ +} + +void +DWARFDebugRanges::Extract(SymbolFileDWARF* dwarf2Data) +{ + RangeList range_list; + dw_offset_t offset = 0; + dw_offset_t debug_ranges_offset = offset; + while (range_list.Extract(dwarf2Data, &offset)) + { + m_range_map[debug_ranges_offset] = range_list; + debug_ranges_offset = offset; + } +} + +bool +DWARFDebugRanges::RangeList::AddRange(dw_addr_t lo_addr, dw_addr_t hi_addr) +{ + if (lo_addr <= hi_addr) + { + Range range(lo_addr, hi_addr); + ranges.push_back(range); + return true; + } + return false; +} + +const DWARFDebugRanges::Range* +DWARFDebugRanges::RangeList::Lookup(dw_addr_t offset) const +{ + Range::const_iterator pos = ranges.begin(); + Range::const_iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + if (pos->begin_offset <= offset && offset < pos->end_offset) + { + return &(*pos); + } + } + return NULL; +} + +size_t +DWARFDebugRanges::RangeList::Size() const +{ + return ranges.size(); +} + +void +DWARFDebugRanges::RangeList::AddOffset(dw_addr_t offset) +{ + if (!ranges.empty()) + { + Range::iterator pos = ranges.begin(); + Range::iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + // assert for unsigned overflows + assert (~pos->begin_offset >= offset); + assert (~pos->end_offset >= offset); + pos->begin_offset += offset; + pos->end_offset += offset; + } + } +} + +void +DWARFDebugRanges::RangeList::SubtractOffset(dw_addr_t offset) +{ + if (!ranges.empty()) + { + Range::iterator pos = ranges.begin(); + Range::iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + assert (pos->begin_offset >= offset); + assert (pos->end_offset >= offset); + pos->begin_offset -= offset; + pos->end_offset -= offset; + } + } +} + + +const DWARFDebugRanges::Range* +DWARFDebugRanges::RangeList::RangeAtIndex(size_t i) const +{ + if (i < ranges.size()) + return &ranges[i]; + return NULL; +} + +bool +DWARFDebugRanges::RangeList::Extract(SymbolFileDWARF* dwarf2Data, uint32_t* offset_ptr) +{ + Clear(); + uint32_t range_offset = *offset_ptr; + const DataExtractor& debug_ranges_data = dwarf2Data->get_debug_ranges_data(); + uint32_t addr_size = debug_ranges_data.GetAddressByteSize(); + + while (debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) + { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + if (!begin && !end) + { + // End of range list + break; + } + // Extend 4 byte addresses that consits of 32 bits of 1's to be 64 bits + // of ones + switch (addr_size) + { + case 2: + if (begin == 0xFFFFull) + begin = DW_INVALID_ADDRESS; + break; + + case 4: + if (begin == 0xFFFFFFFFull) + begin = DW_INVALID_ADDRESS; + break; + + case 8: + break; + + default: + assert(!"DWARFDebugRanges::RangeList::Extract() unsupported address size."); + break; + } + + // Filter out empty ranges + if (begin != end) + ranges.push_back(Range(begin, end)); + } + + // Make sure we consumed at least something + return range_offset != *offset_ptr; +} + + +dw_addr_t +DWARFDebugRanges::RangeList::LowestAddress(const dw_addr_t cu_base_addr) const +{ + dw_addr_t addr = DW_INVALID_ADDRESS; + dw_addr_t curr_base_addr = cu_base_addr; + if (!ranges.empty()) + { + Range::const_iterator pos = ranges.begin(); + Range::const_iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + if (pos->begin_offset == DW_INVALID_ADDRESS) + curr_base_addr = pos->end_offset; + else if (curr_base_addr != DW_INVALID_ADDRESS) + { + dw_addr_t curr_addr = curr_base_addr + pos->begin_offset; + if (addr > curr_addr) + addr = curr_addr; + } + } + } + return addr; +} + +dw_addr_t +DWARFDebugRanges::RangeList::HighestAddress(const dw_addr_t cu_base_addr) const +{ + dw_addr_t addr = 0; + dw_addr_t curr_base_addr = cu_base_addr; + if (!ranges.empty()) + { + Range::const_iterator pos = ranges.begin(); + Range::const_iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + if (pos->begin_offset == DW_INVALID_ADDRESS) + curr_base_addr = pos->end_offset; + else if (curr_base_addr != DW_INVALID_ADDRESS) + { + dw_addr_t curr_addr = curr_base_addr + pos->end_offset; + if (addr < curr_addr) + addr = curr_addr; + } + } + } + if (addr != 0) + return addr; + return DW_INVALID_ADDRESS; +} + + +void +DWARFDebugRanges::Dump(Stream *s, const DataExtractor& debug_ranges_data, uint32_t* offset_ptr, dw_addr_t cu_base_addr) +{ + uint32_t addr_size = s->GetAddressByteSize(); + bool verbose = s->GetVerbose(); + + dw_addr_t base_addr = cu_base_addr; + while (debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) + { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + // Extend 4 byte addresses that consits of 32 bits of 1's to be 64 bits + // of ones + if (begin == 0xFFFFFFFFull && addr_size == 4) + begin = DW_INVALID_ADDRESS; + + s->Indent(); + if (verbose) + { + s->AddressRange(begin, end, sizeof (dw_addr_t), " offsets = "); + } + + + if (begin == 0 && end == 0) + { + s->PutCString(" End"); + break; + } + else if (begin == DW_INVALID_ADDRESS) + { + // A base address selection entry + base_addr = end; + s->Address(base_addr, sizeof (dw_addr_t), " Base address = "); + } + else + { + // Convert from offset to an address + dw_addr_t begin_addr = begin + base_addr; + dw_addr_t end_addr = end + base_addr; + + s->AddressRange(begin_addr, end_addr, sizeof (dw_addr_t), verbose ? " ==> addrs = " : NULL); + } + } +} + +bool +DWARFDebugRanges::FindRanges(dw_offset_t debug_ranges_offset, RangeList& range_list) const +{ + range_map_const_iterator pos = m_range_map.find(debug_ranges_offset); + if (pos != m_range_map.end()) + { + range_list = pos->second; + return true; + } + return false; +} + + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h new file mode 100644 index 000000000000..607c3c24a3ea --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h @@ -0,0 +1,89 @@ +//===-- DWARFDebugRanges.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDebugRanges_h_ +#define liblldb_DWARFDebugRanges_h_ + +#include "SymbolFileDWARF.h" +#include +#include + + +class DWARFDebugRanges +{ +public: + + //------------------------------------------------------------------ + // Address range + //------------------------------------------------------------------ + struct Range + { + Range(dw_addr_t begin = DW_INVALID_ADDRESS, dw_addr_t end = DW_INVALID_ADDRESS) : + begin_offset(begin), + end_offset(end) + { + } + + void Clear() + { + begin_offset = DW_INVALID_ADDRESS; + end_offset = DW_INVALID_ADDRESS; + } + + dw_addr_t begin_offset; + dw_addr_t end_offset; + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + }; + + //------------------------------------------------------------------ + // Collection of ranges + //------------------------------------------------------------------ + struct RangeList + { + RangeList() : + ranges() + { + } + + bool Extract(SymbolFileDWARF* dwarf2Data, uint32_t* offset_ptr); + bool AddRange(dw_addr_t lo_addr, dw_addr_t hi_addr); + void Clear() + { + ranges.clear(); + } + + dw_addr_t LowestAddress(const dw_addr_t base_addr) const; + dw_addr_t HighestAddress(const dw_addr_t base_addr) const; + void AddOffset(dw_addr_t offset); + void SubtractOffset(dw_addr_t offset); + size_t Size() const; + const Range* RangeAtIndex(size_t i) const; + const Range* Lookup(dw_addr_t offset) const; + Range::collection ranges; + }; + + DWARFDebugRanges(); + ~DWARFDebugRanges(); + void Extract(SymbolFileDWARF* dwarf2Data); + static void Dump(lldb_private::Stream *s, const lldb_private::DataExtractor& debug_ranges_data, uint32_t* offset_ptr, dw_addr_t cu_base_addr); + bool FindRanges(dw_offset_t debug_ranges_offset, DWARFDebugRanges::RangeList& range_list) const; + +protected: + typedef std::map range_map; + typedef range_map::iterator range_map_iterator; + typedef range_map::const_iterator range_map_const_iterator; + range_map m_range_map; +}; + + +#endif // liblldb_DWARFDebugRanges_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c new file mode 100644 index 000000000000..fe487f9b7923 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c @@ -0,0 +1,2224 @@ +//===-- DWARFDefines.c ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDefines.h" +#include + +#define DW_TAG_PREFIX "TAG_" +#define DW_AT_PREFIX " AT_" +#define DW_FORM_PREFIX "FORM_" + +/* [7.5.4] Figure 16 "Tag Encodings" (pp. 125-127) in DWARFv3 draft 8 */ + +const char * +DW_TAG_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0000: return DW_TAG_PREFIX "NULL"; + case 0x0001: return DW_TAG_PREFIX "array_type"; + case 0x0002: return DW_TAG_PREFIX "class_type"; + case 0x0003: return DW_TAG_PREFIX "entry_point"; + case 0x0004: return DW_TAG_PREFIX "enumeration_type"; + case 0x0005: return DW_TAG_PREFIX "formal_parameter"; + case 0x0008: return DW_TAG_PREFIX "imported_declaration"; + case 0x000a: return DW_TAG_PREFIX "label"; + case 0x000b: return DW_TAG_PREFIX "lexical_block"; + case 0x000d: return DW_TAG_PREFIX "member"; + case 0x000f: return DW_TAG_PREFIX "pointer_type"; + case 0x0010: return DW_TAG_PREFIX "reference_type"; + case 0x0011: return DW_TAG_PREFIX "compile_unit"; + case 0x0012: return DW_TAG_PREFIX "string_type"; + case 0x0013: return DW_TAG_PREFIX "structure_type"; + case 0x0015: return DW_TAG_PREFIX "subroutine_type"; + case 0x0016: return DW_TAG_PREFIX "typedef"; + case 0x0017: return DW_TAG_PREFIX "union_type"; + case 0x0018: return DW_TAG_PREFIX "unspecified_parameters"; + case 0x0019: return DW_TAG_PREFIX "variant"; + case 0x001a: return DW_TAG_PREFIX "common_block"; + case 0x001b: return DW_TAG_PREFIX "common_inclusion"; + case 0x001c: return DW_TAG_PREFIX "inheritance"; + case 0x001d: return DW_TAG_PREFIX "inlined_subroutine"; + case 0x001e: return DW_TAG_PREFIX "module"; + case 0x001f: return DW_TAG_PREFIX "ptr_to_member_type"; + case 0x0020: return DW_TAG_PREFIX "set_type"; + case 0x0021: return DW_TAG_PREFIX "subrange_type"; + case 0x0022: return DW_TAG_PREFIX "with_stmt"; + case 0x0023: return DW_TAG_PREFIX "access_declaration"; + case 0x0024: return DW_TAG_PREFIX "base_type"; + case 0x0025: return DW_TAG_PREFIX "catch_block"; + case 0x0026: return DW_TAG_PREFIX "const_type"; + case 0x0027: return DW_TAG_PREFIX "constant"; + case 0x0028: return DW_TAG_PREFIX "enumerator"; + case 0x0029: return DW_TAG_PREFIX "file_type"; + case 0x002a: return DW_TAG_PREFIX "friend"; + case 0x002b: return DW_TAG_PREFIX "namelist"; + case 0x002c: return DW_TAG_PREFIX "namelist_item"; + case 0x002d: return DW_TAG_PREFIX "packed_type"; + case 0x002e: return DW_TAG_PREFIX "subprogram"; + case 0x002f: return DW_TAG_PREFIX "template_type_parameter"; + case 0x0030: return DW_TAG_PREFIX "template_value_parameter"; + case 0x0031: return DW_TAG_PREFIX "thrown_type"; + case 0x0032: return DW_TAG_PREFIX "try_block"; + case 0x0033: return DW_TAG_PREFIX "variant_part"; + case 0x0034: return DW_TAG_PREFIX "variable"; + case 0x0035: return DW_TAG_PREFIX "volatile_type"; + case 0x0036: return DW_TAG_PREFIX "dwarf_procedure"; + case 0x0037: return DW_TAG_PREFIX "restrict_type"; + case 0x0038: return DW_TAG_PREFIX "interface_type"; + case 0x0039: return DW_TAG_PREFIX "namespace"; + case 0x003a: return DW_TAG_PREFIX "imported_module"; + case 0x003b: return DW_TAG_PREFIX "unspecified_type"; + case 0x003c: return DW_TAG_PREFIX "partial_unit"; + case 0x003d: return DW_TAG_PREFIX "imported_unit"; +// case 0x003d: return DW_TAG_PREFIX "condition"; + case 0x0040: return DW_TAG_PREFIX "shared_type"; + case 0x4080: return DW_TAG_PREFIX "lo_user"; + case 0xffff: return DW_TAG_PREFIX "hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_TAG_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "array type"; + case 0x0002: return "class type"; + case 0x0003: return "entry point"; + case 0x0004: return "enumeration type"; + case 0x0005: return "formal parameter"; + case 0x0008: return "imported declaration"; + case 0x000a: return "label"; + case 0x000b: return "lexical block"; + case 0x000d: return "member"; + case 0x000f: return "pointer type"; + case 0x0010: return "reference type"; + case 0x0011: return "file"; + case 0x0012: return "string type"; + case 0x0013: return "structure type"; + case 0x0015: return "subroutine type"; + case 0x0016: return "typedef"; + case 0x0017: return "union type"; + case 0x0018: return "unspecified parameters"; + case 0x0019: return "variant"; + case 0x001a: return "common block"; + case 0x001b: return "common inclusion"; + case 0x001c: return "inheritance"; + case 0x001d: return "inlined subroutine"; + case 0x001e: return "module"; + case 0x001f: return "ptr to member type"; + case 0x0020: return "set type"; + case 0x0021: return "subrange type"; + case 0x0022: return "with stmt"; + case 0x0023: return "access declaration"; + case 0x0024: return "base type"; + case 0x0025: return "catch block"; + case 0x0026: return "const type"; + case 0x0027: return "constant"; + case 0x0028: return "enumerator"; + case 0x0029: return "file type"; + case 0x002a: return "friend"; + case 0x002b: return "namelist"; + case 0x002c: return "namelist item"; + case 0x002d: return "packed type"; + case 0x002e: return "function"; + case 0x002f: return "template type parameter"; + case 0x0030: return "template value parameter"; + case 0x0031: return "thrown type"; + case 0x0032: return "try block"; + case 0x0033: return "variant part"; + case 0x0034: return "variable"; + case 0x0035: return "volatile type"; + case 0x0036: return "dwarf procedure"; + case 0x0037: return "restrict type"; + case 0x0038: return "interface type"; + case 0x0039: return "namespace"; + case 0x003a: return "imported module"; + case 0x003b: return "unspecified type"; + case 0x003c: return "partial unit"; + case 0x003d: return "imported unit"; +// case 0x003d: return "condition"; + case 0x0040: return "shared type"; + case 0x4080: return "lo user"; + case 0xffff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_TAG_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0001: return 0; + case 0x0002: return 0; + case 0x0003: return 0; + case 0x0004: return 0; + case 0x0005: return 0; + case 0x0008: return 0; + case 0x000a: return 0; + case 0x000b: return 0; + case 0x000d: return 0; + case 0x000f: return 0; + case 0x0010: return 0; + case 0x0011: return 0; + case 0x0012: return 0; + case 0x0013: return 0; + case 0x0015: return 0; + case 0x0016: return 0; + case 0x0017: return 0; + case 0x0018: return 0; + case 0x0019: return 0; + case 0x001a: return 0; + case 0x001b: return 0; + case 0x001c: return 0; + case 0x001d: return 0; + case 0x001e: return 0; + case 0x001f: return 0; + case 0x0020: return 0; + case 0x0021: return 0; + case 0x0022: return 0; + case 0x0023: return 0; + case 0x0024: return 0; + case 0x0025: return 0; + case 0x0026: return 0; + case 0x0027: return 0; + case 0x0028: return 0; + case 0x0029: return 0; + case 0x002a: return 0; + case 0x002b: return 0; + case 0x002c: return 0; + case 0x002d: return 0; + case 0x002e: return 0; + case 0x002f: return 0; + case 0x0030: return 0; + case 0x0031: return 0; + case 0x0032: return 0; + case 0x0033: return 0; + case 0x0034: return 0; + case 0x0035: return 0; + case 0x0036: return DRC_DWARFv3; + case 0x0037: return DRC_DWARFv3; + case 0x0038: return DRC_DWARFv3; + case 0x0039: return DRC_DWARFv3; + case 0x003a: return DRC_DWARFv3; + case 0x003b: return DRC_DWARFv3; + case 0x003c: return DRC_DWARFv3; + case 0x003d: return DRC_DWARFv3; +// case 0x003d: return DRC_DWARFv3; + case 0x0040: return DRC_DWARFv3; + case 0x4080: return 0; + case 0xffff: return 0; + default: return 0; + } +} + +/* [7.5.4] Figure 17 "Child determination encodings" (p. 128) in DWARFv3 draft 8 */ + +const char * +DW_CHILDREN_value_to_name (uint8_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_CHILDREN_no"; + case 0x1: return "DW_CHILDREN_yes"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CHILDREN constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_CHILDREN_value_to_englishy_name (uint8_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "no"; + case 0x1: return "yes"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CHILDREN constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_CHILDREN_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + default: return 0; + } +} + +/* [7.5.4] Figure 18 "Attribute encodings" (pp. 129-132) in DWARFv3 draft 8 */ + +const char * +DW_AT_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return DW_AT_PREFIX "sibling"; + case 0x0002: return DW_AT_PREFIX "location"; + case 0x0003: return DW_AT_PREFIX "name"; + case 0x0009: return DW_AT_PREFIX "ordering"; + case 0x000b: return DW_AT_PREFIX "byte_size"; + case 0x000c: return DW_AT_PREFIX "bit_offset"; + case 0x000d: return DW_AT_PREFIX "bit_size"; + case 0x0010: return DW_AT_PREFIX "stmt_list"; + case 0x0011: return DW_AT_PREFIX "low_pc"; + case 0x0012: return DW_AT_PREFIX "high_pc"; + case 0x0013: return DW_AT_PREFIX "language"; + case 0x0015: return DW_AT_PREFIX "discr"; + case 0x0016: return DW_AT_PREFIX "discr_value"; + case 0x0017: return DW_AT_PREFIX "visibility"; + case 0x0018: return DW_AT_PREFIX "import"; + case 0x0019: return DW_AT_PREFIX "string_length"; + case 0x001a: return DW_AT_PREFIX "common_reference"; + case 0x001b: return DW_AT_PREFIX "comp_dir"; + case 0x001c: return DW_AT_PREFIX "const_value"; + case 0x001d: return DW_AT_PREFIX "containing_type"; + case 0x001e: return DW_AT_PREFIX "default_value"; + case 0x0020: return DW_AT_PREFIX "inline"; + case 0x0021: return DW_AT_PREFIX "is_optional"; + case 0x0022: return DW_AT_PREFIX "lower_bound"; + case 0x0025: return DW_AT_PREFIX "producer"; + case 0x0027: return DW_AT_PREFIX "prototyped"; + case 0x002a: return DW_AT_PREFIX "return_addr"; + case 0x002c: return DW_AT_PREFIX "start_scope"; + case 0x002e: return DW_AT_PREFIX "bit_stride"; + case 0x002f: return DW_AT_PREFIX "upper_bound"; + case 0x0031: return DW_AT_PREFIX "abstract_origin"; + case 0x0032: return DW_AT_PREFIX "accessibility"; + case 0x0033: return DW_AT_PREFIX "address_class"; + case 0x0034: return DW_AT_PREFIX "artificial"; + case 0x0035: return DW_AT_PREFIX "base_types"; + case 0x0036: return DW_AT_PREFIX "calling_convention"; + case 0x0037: return DW_AT_PREFIX "count"; + case 0x0038: return DW_AT_PREFIX "data_member_location"; + case 0x0039: return DW_AT_PREFIX "decl_column"; + case 0x003a: return DW_AT_PREFIX "decl_file"; + case 0x003b: return DW_AT_PREFIX "decl_line"; + case 0x003c: return DW_AT_PREFIX "declaration"; + case 0x003d: return DW_AT_PREFIX "discr_list"; + case 0x003e: return DW_AT_PREFIX "encoding"; + case 0x003f: return DW_AT_PREFIX "external"; + case 0x0040: return DW_AT_PREFIX "frame_base"; + case 0x0041: return DW_AT_PREFIX "friend"; + case 0x0042: return DW_AT_PREFIX "identifier_case"; + case 0x0043: return DW_AT_PREFIX "macro_info"; + case 0x0044: return DW_AT_PREFIX "namelist_item"; + case 0x0045: return DW_AT_PREFIX "priority"; + case 0x0046: return DW_AT_PREFIX "segment"; + case 0x0047: return DW_AT_PREFIX "specification"; + case 0x0048: return DW_AT_PREFIX "static_link"; + case 0x0049: return DW_AT_PREFIX "type"; + case 0x004a: return DW_AT_PREFIX "use_location"; + case 0x004b: return DW_AT_PREFIX "variable_parameter"; + case 0x004c: return DW_AT_PREFIX "virtuality"; + case 0x004d: return DW_AT_PREFIX "vtable_elem_location"; + case 0x004e: return DW_AT_PREFIX "allocated"; + case 0x004f: return DW_AT_PREFIX "associated"; + case 0x0050: return DW_AT_PREFIX "data_location"; + case 0x0051: return DW_AT_PREFIX "byte_stride"; + case 0x0052: return DW_AT_PREFIX "entry_pc"; + case 0x0053: return DW_AT_PREFIX "use_UTF8"; + case 0x0054: return DW_AT_PREFIX "extension"; + case 0x0055: return DW_AT_PREFIX "ranges"; + case 0x0056: return DW_AT_PREFIX "trampoline"; + case 0x0057: return DW_AT_PREFIX "call_column"; + case 0x0058: return DW_AT_PREFIX "call_file"; + case 0x0059: return DW_AT_PREFIX "call_line"; + case 0x005a: return DW_AT_PREFIX "description"; + case 0x005b: return DW_AT_PREFIX "binary_scale"; + case 0x005c: return DW_AT_PREFIX "decimal_scale"; + case 0x005d: return DW_AT_PREFIX "small"; + case 0x005e: return DW_AT_PREFIX "decimal_sign"; + case 0x005f: return DW_AT_PREFIX "digit_count"; + case 0x0060: return DW_AT_PREFIX "picture_string"; + case 0x0061: return DW_AT_PREFIX "mutable"; + case 0x0062: return DW_AT_PREFIX "threads_scaled"; + case 0x0063: return DW_AT_PREFIX "explicit"; + case 0x0064: return DW_AT_PREFIX "object_pointer"; + case 0x0065: return DW_AT_PREFIX "endianity"; + case 0x0066: return DW_AT_PREFIX "elemental"; + case 0x0067: return DW_AT_PREFIX "pure"; + case 0x0068: return DW_AT_PREFIX "recursive"; + case 0x2000: return DW_AT_PREFIX "lo_user"; + case 0x3fff: return DW_AT_PREFIX "hi_user"; + case 0x2001: return DW_AT_PREFIX "MIPS_fde"; + case 0x2002: return DW_AT_PREFIX "MIPS_loop_begin"; + case 0x2003: return DW_AT_PREFIX "MIPS_tail_loop_begin"; + case 0x2004: return DW_AT_PREFIX "MIPS_epilog_begin"; + case 0x2005: return DW_AT_PREFIX "MIPS_loop_unroll_factor"; + case 0x2006: return DW_AT_PREFIX "MIPS_software_pipeline_depth"; + case 0x2007: return DW_AT_PREFIX "MIPS_linkage_name"; + case 0x2008: return DW_AT_PREFIX "MIPS_stride"; + case 0x2009: return DW_AT_PREFIX "MIPS_abstract_name"; + case 0x200a: return DW_AT_PREFIX "MIPS_clone_origin"; + case 0x200b: return DW_AT_PREFIX "MIPS_has_inlines"; + case 0x2101: return DW_AT_PREFIX "sf_names"; + case 0x2102: return DW_AT_PREFIX "src_info"; + case 0x2103: return DW_AT_PREFIX "mac_info"; + case 0x2104: return DW_AT_PREFIX "src_coords"; + case 0x2105: return DW_AT_PREFIX "body_begin"; + case 0x2106: return DW_AT_PREFIX "body_end"; + case 0x2107: return DW_AT_PREFIX "GNU_vector"; + case 0x2501: return DW_AT_PREFIX "APPLE_repository_file"; + case 0x2502: return DW_AT_PREFIX "APPLE_repository_type"; + case 0x2503: return DW_AT_PREFIX "APPLE_repository_name"; + case 0x2504: return DW_AT_PREFIX "APPLE_repository_specification"; + case 0x2505: return DW_AT_PREFIX "APPLE_repository_import"; + case 0x2506: return DW_AT_PREFIX "APPLE_repository_abstract_origin"; + case DW_AT_APPLE_flags: return DW_AT_PREFIX "APPLE_flags"; + case DW_AT_APPLE_optimized: return DW_AT_PREFIX "APPLE_optimized"; + case DW_AT_APPLE_isa: return DW_AT_PREFIX "APPLE_isa"; + case DW_AT_APPLE_block: return DW_AT_PREFIX "APPLE_block"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_AT_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "sibling"; + case 0x0002: return "location"; + case 0x0003: return "name"; + case 0x0009: return "ordering"; + case 0x000b: return "byte size"; + case 0x000c: return "bit offset"; + case 0x000d: return "bit size"; + case 0x0010: return "stmt list"; + case 0x0011: return "low pc"; + case 0x0012: return "high pc"; + case 0x0013: return "language"; + case 0x0015: return "discr"; + case 0x0016: return "discr value"; + case 0x0017: return "visibility"; + case 0x0018: return "import"; + case 0x0019: return "string length"; + case 0x001a: return "common reference"; + case 0x001b: return "comp dir"; + case 0x001c: return "const value"; + case 0x001d: return "containing type"; + case 0x001e: return "default value"; + case 0x0020: return "inline"; + case 0x0021: return "is optional"; + case 0x0022: return "lower bound"; + case 0x0025: return "producer"; + case 0x0027: return "prototyped"; + case 0x002a: return "return addr"; + case 0x002c: return "start scope"; + case 0x002e: return "bit stride"; + case 0x002f: return "upper bound"; + case 0x0031: return "abstract origin"; + case 0x0032: return "accessibility"; + case 0x0033: return "address class"; + case 0x0034: return "artificial"; + case 0x0035: return "base types"; + case 0x0036: return "calling convention"; + case 0x0037: return "count"; + case 0x0038: return "data member location"; + case 0x0039: return "decl column"; + case 0x003a: return "decl file"; + case 0x003b: return "decl line"; + case 0x003c: return "declaration"; + case 0x003d: return "discr list"; + case 0x003e: return "encoding"; + case 0x003f: return "external"; + case 0x0040: return "frame base"; + case 0x0041: return "friend"; + case 0x0042: return "identifier case"; + case 0x0043: return "macro info"; + case 0x0044: return "namelist item"; + case 0x0045: return "priority"; + case 0x0046: return "segment"; + case 0x0047: return "specification"; + case 0x0048: return "static link"; + case 0x0049: return "type"; + case 0x004a: return "use location"; + case 0x004b: return "variable parameter"; + case 0x004c: return "virtuality"; + case 0x004d: return "vtable elem location"; + case 0x004e: return "allocated"; + case 0x004f: return "associated"; + case 0x0050: return "data location"; + case 0x0051: return "byte stride"; + case 0x0052: return "entry pc"; + case 0x0053: return "use UTF8"; + case 0x0054: return "extension"; + case 0x0055: return "ranges"; + case 0x0056: return "trampoline"; + case 0x0057: return "call column"; + case 0x0058: return "call file"; + case 0x0059: return "call line"; + case 0x005a: return "description"; + case 0x005b: return "binary scale"; + case 0x005c: return "decimal scale"; + case 0x005d: return "small"; + case 0x005e: return "decimal sign"; + case 0x005f: return "digit count"; + case 0x0060: return "picture string"; + case 0x0061: return "mutable"; + case 0x0062: return "threads scaled"; + case 0x0063: return "explicit"; + case 0x0064: return "object pointer"; + case 0x0065: return "endianity"; + case 0x0066: return "elemental"; + case 0x0067: return "pure"; + case 0x0068: return "recursive"; + case 0x2000: return "lo user"; + case 0x3fff: return "hi user"; + case 0x2001: return "MIPS fde"; + case 0x2002: return "MIPS loop begin"; + case 0x2003: return "MIPS tail loop begin"; + case 0x2004: return "MIPS epilog begin"; + case 0x2005: return "MIPS loop unroll factor"; + case 0x2006: return "MIPS software pipeline depth"; + case 0x2007: return "MIPS linkage name"; + case 0x2008: return "MIPS stride"; + case 0x2009: return "MIPS abstract name"; + case 0x200a: return "MIPS clone origin"; + case 0x200b: return "MIPS has inlines"; + case 0x2101: return "source file names"; + case 0x2102: return "source info"; + case 0x2103: return "macro info"; + case 0x2104: return "source coordinates"; + case 0x2105: return "body begin"; + case 0x2106: return "body end"; + case 0x2107: return "GNU vector"; + case 0x2501: return "repository file"; + case 0x2502: return "repository type"; + case 0x2503: return "repository name"; + case 0x2504: return "repository specification"; + case 0x2505: return "repository import"; + case 0x2506: return "repository abstract origin"; + case DW_AT_APPLE_flags: return "Apple gcc compiler flags"; + case DW_AT_APPLE_optimized: return "APPLE optimized"; + case DW_AT_APPLE_isa: return "APPLE instruction set architecture"; + case DW_AT_APPLE_block: return "APPLE block"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_AT_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0001: return DRC_REFERENCE; + case 0x0002: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0003: return DRC_STRING; + case 0x0009: return DRC_CONSTANT; + case 0x000b: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x000c: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x000d: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0010: return DRC_LINEPTR; + case 0x0011: return DRC_ADDRESS; + case 0x0012: return DRC_ADDRESS; + case 0x0013: return DRC_CONSTANT; + case 0x0015: return DRC_REFERENCE; + case 0x0016: return DRC_CONSTANT; + case 0x0017: return DRC_CONSTANT; + case 0x0018: return DRC_REFERENCE; + case 0x0019: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x001a: return DRC_REFERENCE; + case 0x001b: return DRC_STRING; + case 0x001c: return DRC_BLOCK | DRC_CONSTANT | DRC_STRING; + case 0x001d: return DRC_REFERENCE; + case 0x001e: return DRC_REFERENCE; + case 0x0020: return DRC_CONSTANT; + case 0x0021: return DRC_FLAG; + case 0x0022: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0025: return DRC_STRING; + case 0x0027: return DRC_FLAG; + case 0x002a: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x002c: return DRC_CONSTANT; + case 0x002e: return DRC_CONSTANT; + case 0x002f: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0031: return DRC_REFERENCE; + case 0x0032: return DRC_CONSTANT; + case 0x0033: return DRC_CONSTANT; + case 0x0034: return DRC_FLAG; + case 0x0035: return DRC_REFERENCE; + case 0x0036: return DRC_CONSTANT; + case 0x0037: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0038: return DRC_BLOCK | DRC_CONSTANT | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0039: return DRC_CONSTANT; + case 0x003a: return DRC_CONSTANT; + case 0x003b: return DRC_CONSTANT; + case 0x003c: return DRC_FLAG; + case 0x003d: return DRC_BLOCK; + case 0x003e: return DRC_CONSTANT; + case 0x003f: return DRC_FLAG; + case 0x0040: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0041: return DRC_REFERENCE; + case 0x0042: return DRC_CONSTANT; + case 0x0043: return DRC_MACPTR; + case 0x0044: return DRC_BLOCK; + case 0x0045: return DRC_REFERENCE; + case 0x0046: return DRC_BLOCK | DRC_CONSTANT; + case 0x0047: return DRC_REFERENCE; + case 0x0048: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0049: return DRC_REFERENCE; + case 0x004a: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x004b: return DRC_FLAG; + case 0x004c: return DRC_CONSTANT; + case 0x004d: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x004e: return DRC_BLOCK | DRC_CONSTANT | DRC_DWARFv3 | DRC_REFERENCE; + case 0x004f: return DRC_BLOCK | DRC_CONSTANT | DRC_DWARFv3 | DRC_REFERENCE; + case 0x0050: return DRC_BLOCK | DRC_DWARFv3; + case 0x0051: return DRC_BLOCK | DRC_CONSTANT | DRC_DWARFv3 | DRC_REFERENCE; + case 0x0052: return DRC_ADDRESS | DRC_DWARFv3; + case 0x0053: return DRC_DWARFv3 | DRC_FLAG; + case 0x0054: return DRC_DWARFv3 | DRC_REFERENCE; + case 0x0055: return DRC_DWARFv3 | DRC_RANGELISTPTR; + case 0x0056: return DRC_ADDRESS | DRC_DWARFv3 | DRC_FLAG | DRC_REFERENCE | DRC_STRING; + case 0x0057: return DRC_CONSTANT | DRC_DWARFv3; + case 0x0058: return DRC_CONSTANT | DRC_DWARFv3; + case 0x0059: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005a: return DRC_DWARFv3 | DRC_STRING; + case 0x005b: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005c: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005d: return DRC_DWARFv3 | DRC_REFERENCE; + case 0x005e: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005f: return DRC_CONSTANT | DRC_DWARFv3; + case 0x0060: return DRC_DWARFv3 | DRC_STRING; + case 0x0061: return DRC_DWARFv3 | DRC_FLAG; + case 0x0062: return DRC_DWARFv3 | DRC_FLAG; + case 0x0063: return DRC_DWARFv3 | DRC_FLAG; + case 0x0064: return DRC_DWARFv3 | DRC_REFERENCE; + case 0x0065: return DRC_0x65 | DRC_CONSTANT | DRC_DWARFv3; + case 0x0066: return DRC_DWARFv3 | DRC_FLAG; + case 0x0067: return DRC_DWARFv3 | DRC_FLAG; + case 0x0068: return DRC_DWARFv3 | DRC_FLAG; + case 0x2000: return 0; + case 0x3fff: return 0; + case 0x2001: return DRC_VENDOR_MIPS; + case 0x2002: return DRC_VENDOR_MIPS; + case 0x2003: return DRC_VENDOR_MIPS; + case 0x2004: return DRC_VENDOR_MIPS; + case 0x2005: return DRC_VENDOR_MIPS; + case 0x2006: return DRC_VENDOR_MIPS; + case 0x2007: return DRC_STRING | DRC_VENDOR_MIPS; + case 0x2008: return DRC_VENDOR_MIPS; + case 0x2009: return DRC_VENDOR_MIPS; + case 0x200a: return DRC_VENDOR_MIPS; + case 0x200b: return DRC_VENDOR_MIPS; + default: return 0; + } +} + +/* [7.5.4] Figure 19 "Attribute form encodings" (pp. 133-134) in DWARFv3 draft 8 */ + +const char * +DW_FORM_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return DW_FORM_PREFIX "addr"; + case 0x03: return DW_FORM_PREFIX "block2"; + case 0x04: return DW_FORM_PREFIX "block4"; + case 0x05: return DW_FORM_PREFIX "data2"; + case 0x06: return DW_FORM_PREFIX "data4"; + case 0x07: return DW_FORM_PREFIX "data8"; + case 0x08: return DW_FORM_PREFIX "string"; + case 0x09: return DW_FORM_PREFIX "block"; + case 0x0a: return DW_FORM_PREFIX "block1"; + case 0x0b: return DW_FORM_PREFIX "data1"; + case 0x0c: return DW_FORM_PREFIX "flag"; + case 0x0d: return DW_FORM_PREFIX "sdata"; + case 0x0e: return DW_FORM_PREFIX "strp"; + case 0x0f: return DW_FORM_PREFIX "udata"; + case 0x10: return DW_FORM_PREFIX "ref_addr"; + case 0x11: return DW_FORM_PREFIX "ref1"; + case 0x12: return DW_FORM_PREFIX "ref2"; + case 0x13: return DW_FORM_PREFIX "ref4"; + case 0x14: return DW_FORM_PREFIX "ref8"; + case 0x15: return DW_FORM_PREFIX "ref_udata"; + case 0x16: return DW_FORM_PREFIX "indirect"; +// case DW_FORM_APPLE_db_str: return DW_FORM_PREFIX "APPLE_db_str"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_FORM_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "addr"; + case 0x03: return "block2"; + case 0x04: return "block4"; + case 0x05: return "data2"; + case 0x06: return "data4"; + case 0x07: return "data8"; + case 0x08: return "string"; + case 0x09: return "block"; + case 0x0a: return "block1"; + case 0x0b: return "data1"; + case 0x0c: return "flag"; + case 0x0d: return "sdata"; + case 0x0e: return "strp"; + case 0x0f: return "udata"; + case 0x10: return "ref addr"; + case 0x11: return "ref1"; + case 0x12: return "ref2"; + case 0x13: return "ref4"; + case 0x14: return "ref8"; + case 0x15: return "ref udata"; + case 0x16: return "indirect"; +// case DW_FORM_APPLE_db_str: return "repository str"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_FORM_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return DRC_ADDRESS; + case 0x03: return DRC_BLOCK | DRC_LOCEXPR; + case 0x04: return DRC_BLOCK | DRC_LOCEXPR; + case 0x05: return DRC_CONSTANT; + case 0x06: return DRC_CONSTANT | DRC_LINEPTR | DRC_LOCLISTPTR | DRC_MACPTR | DRC_RANGELISTPTR; + case 0x07: return DRC_CONSTANT | DRC_LINEPTR | DRC_LOCLISTPTR | DRC_MACPTR | DRC_RANGELISTPTR; + case 0x08: return DRC_STRING; + case 0x09: return DRC_BLOCK | DRC_LOCEXPR; + case 0x0a: return DRC_BLOCK | DRC_LOCEXPR; + case 0x0b: return DRC_CONSTANT; + case 0x0c: return DRC_FLAG; + case 0x0d: return DRC_CONSTANT; + case 0x0e: return DRC_STRING; + case 0x0f: return DRC_CONSTANT; + case 0x10: return DRC_REFERENCE; + case 0x11: return DRC_REFERENCE; + case 0x12: return DRC_REFERENCE; + case 0x13: return DRC_REFERENCE; + case 0x14: return DRC_REFERENCE; + case 0x15: return DRC_REFERENCE; + case 0x16: return DRC_INDIRECT_SPECIAL; + default: return 0; + } +} + +/* [7.7.1] Figure 22 "DWARF operation encodings" (pp. 136-139) in DWARFv3 draft 8 */ + +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 0xf0: return "DW_OP_APPLE_uninit"; + case 0xe0: return "DW_OP_lo_user"; + case 0xff: return "DW_OP_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_OP_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x03: return "addr"; + case 0x06: return "deref"; + case 0x08: return "const1u"; + case 0x09: return "const1s"; + case 0x0a: return "const2u"; + case 0x0b: return "const2s"; + case 0x0c: return "const4u"; + case 0x0d: return "const4s"; + case 0x0e: return "const8u"; + case 0x0f: return "const8s"; + case 0x10: return "constu"; + case 0x11: return "consts"; + case 0x12: return "dup"; + case 0x13: return "drop"; + case 0x14: return "over"; + case 0x15: return "pick"; + case 0x16: return "swap"; + case 0x17: return "rot"; + case 0x18: return "xderef"; + case 0x19: return "abs"; + case 0x1a: return "and"; + case 0x1b: return "div"; + case 0x1c: return "minus"; + case 0x1d: return "mod"; + case 0x1e: return "mul"; + case 0x1f: return "neg"; + case 0x20: return "not"; + case 0x21: return "or"; + case 0x22: return "plus"; + case 0x23: return "plus uconst"; + case 0x24: return "shl"; + case 0x25: return "shr"; + case 0x26: return "shra"; + case 0x27: return "xor"; + case 0x2f: return "skip"; + case 0x28: return "bra"; + case 0x29: return "eq"; + case 0x2a: return "ge"; + case 0x2b: return "gt"; + case 0x2c: return "le"; + case 0x2d: return "lt"; + case 0x2e: return "ne"; + case 0x30: return "lit0"; + case 0x31: return "lit1"; + case 0x32: return "lit2"; + case 0x33: return "lit3"; + case 0x34: return "lit4"; + case 0x35: return "lit5"; + case 0x36: return "lit6"; + case 0x37: return "lit7"; + case 0x38: return "lit8"; + case 0x39: return "lit9"; + case 0x3a: return "lit10"; + case 0x3b: return "lit11"; + case 0x3c: return "lit12"; + case 0x3d: return "lit13"; + case 0x3e: return "lit14"; + case 0x3f: return "lit15"; + case 0x40: return "lit16"; + case 0x41: return "lit17"; + case 0x42: return "lit18"; + case 0x43: return "lit19"; + case 0x44: return "lit20"; + case 0x45: return "lit21"; + case 0x46: return "lit22"; + case 0x47: return "lit23"; + case 0x48: return "lit24"; + case 0x49: return "lit25"; + case 0x4a: return "lit26"; + case 0x4b: return "lit27"; + case 0x4c: return "lit28"; + case 0x4d: return "lit29"; + case 0x4e: return "lit30"; + case 0x4f: return "lit31"; + case 0x50: return "reg0"; + case 0x51: return "reg1"; + case 0x52: return "reg2"; + case 0x53: return "reg3"; + case 0x54: return "reg4"; + case 0x55: return "reg5"; + case 0x56: return "reg6"; + case 0x57: return "reg7"; + case 0x58: return "reg8"; + case 0x59: return "reg9"; + case 0x5a: return "reg10"; + case 0x5b: return "reg11"; + case 0x5c: return "reg12"; + case 0x5d: return "reg13"; + case 0x5e: return "reg14"; + case 0x5f: return "reg15"; + case 0x60: return "reg16"; + case 0x61: return "reg17"; + case 0x62: return "reg18"; + case 0x63: return "reg19"; + case 0x64: return "reg20"; + case 0x65: return "reg21"; + case 0x66: return "reg22"; + case 0x67: return "reg23"; + case 0x68: return "reg24"; + case 0x69: return "reg25"; + case 0x6a: return "reg26"; + case 0x6b: return "reg27"; + case 0x6c: return "reg28"; + case 0x6d: return "reg29"; + case 0x6e: return "reg30"; + case 0x6f: return "reg31"; + case 0x70: return "breg0"; + case 0x71: return "breg1"; + case 0x72: return "breg2"; + case 0x73: return "breg3"; + case 0x74: return "breg4"; + case 0x75: return "breg5"; + case 0x76: return "breg6"; + case 0x77: return "breg7"; + case 0x78: return "breg8"; + case 0x79: return "breg9"; + case 0x7a: return "breg10"; + case 0x7b: return "breg11"; + case 0x7c: return "breg12"; + case 0x7d: return "breg13"; + case 0x7e: return "breg14"; + case 0x7f: return "breg15"; + case 0x80: return "breg16"; + case 0x81: return "breg17"; + case 0x82: return "breg18"; + case 0x83: return "breg19"; + case 0x84: return "breg20"; + case 0x85: return "breg21"; + case 0x86: return "breg22"; + case 0x87: return "breg23"; + case 0x88: return "breg24"; + case 0x89: return "breg25"; + case 0x8a: return "breg26"; + case 0x8b: return "breg27"; + case 0x8c: return "breg28"; + case 0x8d: return "breg29"; + case 0x8e: return "breg30"; + case 0x8f: return "breg31"; + case 0x90: return "regx"; + case 0x91: return "fbreg"; + case 0x92: return "bregx"; + case 0x93: return "piece"; + case 0x94: return "deref size"; + case 0x95: return "xderef size"; + case 0x96: return "nop"; + case 0x97: return "push object address"; + case 0x98: return "call2"; + case 0x99: return "call4"; + case 0x9a: return "call ref"; + case 0xf0: return "uninitialized"; + case 0xe0: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_OP_value_to_class (uint32_t val) +{ + switch (val) { + case 0x03: return DRC_ONEOPERAND; + case 0x06: return DRC_ZEROOPERANDS; + case 0x08: return DRC_ONEOPERAND; + case 0x09: return DRC_ONEOPERAND; + case 0x0a: return DRC_ONEOPERAND; + case 0x0b: return DRC_ONEOPERAND; + case 0x0c: return DRC_ONEOPERAND; + case 0x0d: return DRC_ONEOPERAND; + case 0x0e: return DRC_ONEOPERAND; + case 0x0f: return DRC_ONEOPERAND; + case 0x10: return DRC_ONEOPERAND; + case 0x11: return DRC_ONEOPERAND; + case 0x12: return DRC_ZEROOPERANDS; + case 0x13: return DRC_ZEROOPERANDS; + case 0x14: return DRC_ZEROOPERANDS; + case 0x15: return DRC_ONEOPERAND; + case 0x16: return DRC_ZEROOPERANDS; + case 0x17: return DRC_ZEROOPERANDS; + case 0x18: return DRC_ZEROOPERANDS; + case 0x19: return DRC_ZEROOPERANDS; + case 0x1a: return DRC_ZEROOPERANDS; + case 0x1b: return DRC_ZEROOPERANDS; + case 0x1c: return DRC_ZEROOPERANDS; + case 0x1d: return DRC_ZEROOPERANDS; + case 0x1e: return DRC_ZEROOPERANDS; + case 0x1f: return DRC_ZEROOPERANDS; + case 0x20: return DRC_ZEROOPERANDS; + case 0x21: return DRC_ZEROOPERANDS; + case 0x22: return DRC_ZEROOPERANDS; + case 0x23: return DRC_ONEOPERAND; + case 0x24: return DRC_ZEROOPERANDS; + case 0x25: return DRC_ZEROOPERANDS; + case 0x26: return DRC_ZEROOPERANDS; + case 0x27: return DRC_ZEROOPERANDS; + case 0x2f: return DRC_ONEOPERAND; + case 0x28: return DRC_ONEOPERAND; + case 0x29: return DRC_ZEROOPERANDS; + case 0x2a: return DRC_ZEROOPERANDS; + case 0x2b: return DRC_ZEROOPERANDS; + case 0x2c: return DRC_ZEROOPERANDS; + case 0x2d: return DRC_ZEROOPERANDS; + case 0x2e: return DRC_ZEROOPERANDS; + case 0x30: return DRC_ZEROOPERANDS; + case 0x31: return DRC_ZEROOPERANDS; + case 0x32: return DRC_ZEROOPERANDS; + case 0x33: return DRC_ZEROOPERANDS; + case 0x34: return DRC_ZEROOPERANDS; + case 0x35: return DRC_ZEROOPERANDS; + case 0x36: return DRC_ZEROOPERANDS; + case 0x37: return DRC_ZEROOPERANDS; + case 0x38: return DRC_ZEROOPERANDS; + case 0x39: return DRC_ZEROOPERANDS; + case 0x3a: return DRC_ZEROOPERANDS; + case 0x3b: return DRC_ZEROOPERANDS; + case 0x3c: return DRC_ZEROOPERANDS; + case 0x3d: return DRC_ZEROOPERANDS; + case 0x3e: return DRC_ZEROOPERANDS; + case 0x3f: return DRC_ZEROOPERANDS; + case 0x40: return DRC_ZEROOPERANDS; + case 0x41: return DRC_ZEROOPERANDS; + case 0x42: return DRC_ZEROOPERANDS; + case 0x43: return DRC_ZEROOPERANDS; + case 0x44: return DRC_ZEROOPERANDS; + case 0x45: return DRC_ZEROOPERANDS; + case 0x46: return DRC_ZEROOPERANDS; + case 0x47: return DRC_ZEROOPERANDS; + case 0x48: return DRC_ZEROOPERANDS; + case 0x49: return DRC_ZEROOPERANDS; + case 0x4a: return DRC_ZEROOPERANDS; + case 0x4b: return DRC_ZEROOPERANDS; + case 0x4c: return DRC_ZEROOPERANDS; + case 0x4d: return DRC_ZEROOPERANDS; + case 0x4e: return DRC_ZEROOPERANDS; + case 0x4f: return DRC_ZEROOPERANDS; + case 0x50: return DRC_ZEROOPERANDS; + case 0x51: return DRC_ZEROOPERANDS; + case 0x52: return DRC_ZEROOPERANDS; + case 0x53: return DRC_ZEROOPERANDS; + case 0x54: return DRC_ZEROOPERANDS; + case 0x55: return DRC_ZEROOPERANDS; + case 0x56: return DRC_ZEROOPERANDS; + case 0x57: return DRC_ZEROOPERANDS; + case 0x58: return DRC_ZEROOPERANDS; + case 0x59: return DRC_ZEROOPERANDS; + case 0x5a: return DRC_ZEROOPERANDS; + case 0x5b: return DRC_ZEROOPERANDS; + case 0x5c: return DRC_ZEROOPERANDS; + case 0x5d: return DRC_ZEROOPERANDS; + case 0x5e: return DRC_ZEROOPERANDS; + case 0x5f: return DRC_ZEROOPERANDS; + case 0x60: return DRC_ZEROOPERANDS; + case 0x61: return DRC_ZEROOPERANDS; + case 0x62: return DRC_ZEROOPERANDS; + case 0x63: return DRC_ZEROOPERANDS; + case 0x64: return DRC_ZEROOPERANDS; + case 0x65: return DRC_ZEROOPERANDS; + case 0x66: return DRC_ZEROOPERANDS; + case 0x67: return DRC_ZEROOPERANDS; + case 0x68: return DRC_ZEROOPERANDS; + case 0x69: return DRC_ZEROOPERANDS; + case 0x6a: return DRC_ZEROOPERANDS; + case 0x6b: return DRC_ZEROOPERANDS; + case 0x6c: return DRC_ZEROOPERANDS; + case 0x6d: return DRC_ZEROOPERANDS; + case 0x6e: return DRC_ZEROOPERANDS; + case 0x6f: return DRC_ZEROOPERANDS; + case 0x70: return DRC_ONEOPERAND; + case 0x71: return DRC_ONEOPERAND; + case 0x72: return DRC_ONEOPERAND; + case 0x73: return DRC_ONEOPERAND; + case 0x74: return DRC_ONEOPERAND; + case 0x75: return DRC_ONEOPERAND; + case 0x76: return DRC_ONEOPERAND; + case 0x77: return DRC_ONEOPERAND; + case 0x78: return DRC_ONEOPERAND; + case 0x79: return DRC_ONEOPERAND; + case 0x7a: return DRC_ONEOPERAND; + case 0x7b: return DRC_ONEOPERAND; + case 0x7c: return DRC_ONEOPERAND; + case 0x7d: return DRC_ONEOPERAND; + case 0x7e: return DRC_ONEOPERAND; + case 0x7f: return DRC_ONEOPERAND; + case 0x80: return DRC_ONEOPERAND; + case 0x81: return DRC_ONEOPERAND; + case 0x82: return DRC_ONEOPERAND; + case 0x83: return DRC_ONEOPERAND; + case 0x84: return DRC_ONEOPERAND; + case 0x85: return DRC_ONEOPERAND; + case 0x86: return DRC_ONEOPERAND; + case 0x87: return DRC_ONEOPERAND; + case 0x88: return DRC_ONEOPERAND; + case 0x89: return DRC_ONEOPERAND; + case 0x8a: return DRC_ONEOPERAND; + case 0x8b: return DRC_ONEOPERAND; + case 0x8c: return DRC_ONEOPERAND; + case 0x8d: return DRC_ONEOPERAND; + case 0x8e: return DRC_ONEOPERAND; + case 0x8f: return DRC_ONEOPERAND; + case 0x90: return DRC_ONEOPERAND; + case 0x91: return DRC_ONEOPERAND; + case 0x92: return DRC_TWOOPERANDS; + case 0x93: return DRC_ONEOPERAND; + case 0x94: return DRC_ONEOPERAND; + case 0x95: return DRC_ONEOPERAND; + case 0x96: return DRC_ZEROOPERANDS; + case 0x97: return DRC_DWARFv3 | DRC_ZEROOPERANDS; + case 0x98: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x99: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x9a: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0xf0: return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */ + case 0xe0: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.8] Figure 23 "Base type encoding values" (pp. 140-141) in DWARFv3 draft 8 */ + +const char * +DW_ATE_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_ATE_address"; + case 0x02: return "DW_ATE_boolean"; + case 0x03: return "DW_ATE_complex_float"; + case 0x04: return "DW_ATE_float"; + case 0x05: return "DW_ATE_signed"; + case 0x06: return "DW_ATE_signed_char"; + case 0x07: return "DW_ATE_unsigned"; + case 0x08: return "DW_ATE_unsigned_char"; + case 0x09: return "DW_ATE_imaginary_float"; + case 0x80: return "DW_ATE_lo_user"; + case 0xff: return "DW_ATE_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ATE_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "address"; + case 0x02: return "boolean"; + case 0x03: return "complex float"; + case 0x04: return "float"; + case 0x05: return "signed"; + case 0x06: return "signed char"; + case 0x07: return "unsigned"; + case 0x08: return "unsigned char"; + case 0x09: return "imaginary float"; + case 0x80: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ATE_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x04: return 0; + case 0x05: return 0; + case 0x06: return 0; + case 0x07: return 0; + case 0x08: return 0; + case 0x09: return DRC_DWARFv3; + case 0x80: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.9] Figure 24 "Accessibility encodings" (p. 141) in DWARFv3 draft 8 */ + +const char * +DW_ACCESS_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "DW_ACCESS_public"; + case 0x2: return "DW_ACCESS_protected"; + case 0x3: return "DW_ACCESS_private"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ACCESS constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ACCESS_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "public"; + case 0x2: return "protected"; + case 0x3: return "private"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ACCESS constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ACCESS_value_to_class (uint32_t val) +{ + switch (val) { + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.10] Figure 25 "Visibility encodings" (p. 142) in DWARFv3 draft 8 */ + +const char * +DW_VIS_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "DW_VIS_local"; + case 0x2: return "DW_VIS_exported"; + case 0x3: return "DW_VIS_qualified"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIS constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_VIS_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "local"; + case 0x2: return "exported"; + case 0x3: return "qualified"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIS constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_VIS_value_to_class (uint32_t val) +{ + switch (val) { + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.11] Figure 26 "Virtuality encodings" (p. 142) in DWARFv3 draft 8 */ + +const char * +DW_VIRTUALITY_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_VIRTUALITY_none"; + case 0x1: return "DW_VIRTUALITY_virtual"; + case 0x2: return "DW_VIRTUALITY_pure_virtual"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIRTUALITY constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_VIRTUALITY_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "none"; + case 0x1: return "virtual"; + case 0x2: return "pure virtual"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIRTUALITY constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_VIRTUALITY_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + case 0x2: return 0; + default: return 0; + } +} + +/* [7.12] Figure 27 "Language encodings" (p. 143) in DWARFv3 draft 8 */ + +const char * +DW_LANG_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "DW_LANG_C89"; + case 0x0002: return "DW_LANG_C"; + case 0x0003: return "DW_LANG_Ada83"; + case 0x0004: return "DW_LANG_C_plus_plus"; + case 0x0005: return "DW_LANG_Cobol74"; + case 0x0006: return "DW_LANG_Cobol85"; + case 0x0007: return "DW_LANG_Fortran77"; + case 0x0008: return "DW_LANG_Fortran90"; + case 0x0009: return "DW_LANG_Pascal83"; + case 0x000a: return "DW_LANG_Modula2"; + case 0x000b: return "DW_LANG_Java"; + case 0x000c: return "DW_LANG_C99"; + case 0x000d: return "DW_LANG_Ada95"; + case 0x000e: return "DW_LANG_Fortran95"; + case 0x000f: return "DW_LANG_PLI"; + case 0x0010: return "DW_LANG_ObjC"; + case 0x0011: return "DW_LANG_ObjC_plus_plus"; + case 0x0012: return "DW_LANG_UPC"; + case 0x0013: return "DW_LANG_D"; + case 0x8000: return "DW_LANG_lo_user"; + case 0x8001: return "DW_LANG_Mips_Assembler"; + case 0x8765: return "DW_LANG_Upc"; + case 0xffff: return "DW_LANG_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_LANG_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "C89"; + case 0x0002: return "C"; + case 0x0003: return "Ada83"; + case 0x0004: return "C++"; + case 0x0005: return "Cobol74"; + case 0x0006: return "Cobol85"; + case 0x0007: return "Fortran77"; + case 0x0008: return "Fortran90"; + case 0x0009: return "Pascal83"; + case 0x000a: return "Modula2"; + case 0x000b: return "Java"; + case 0x000c: return "C99"; + case 0x000d: return "Ada95"; + case 0x000e: return "Fortran95"; + case 0x000f: return "PLI"; + case 0x0010: return "Objective C"; + case 0x0011: return "Objective C++"; + case 0x0012: return "UPC"; + case 0x0013: return "D"; + case 0x8000: return "lo user"; + case 0x8001: return "MIPS Assembler"; + case 0x8765: return "UPC"; + case 0xffff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_LANG_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0001: return 0; + case 0x0002: return 0; + case 0x0003: return 0; + case 0x0004: return 0; + case 0x0005: return 0; + case 0x0006: return 0; + case 0x0007: return 0; + case 0x0008: return 0; + case 0x0009: return 0; + case 0x000a: return 0; + case 0x000b: return DRC_DWARFv3; + case 0x000c: return DRC_DWARFv3; + case 0x000d: return DRC_DWARFv3; + case 0x000e: return DRC_DWARFv3; + case 0x000f: return DRC_DWARFv3; + case 0x0010: return DRC_DWARFv3; + case 0x0011: return DRC_DWARFv3; + case 0x0012: return DRC_DWARFv3; + case 0x0013: return DRC_DWARFv3; + case 0x8000: return 0; + case 0x8001: return 0; + case 0x8765: return 0; + case 0xffff: return 0; + default: return 0; + } +} + +/* [7.13], "Address Class Encodings" (p. 144) in DWARFv3 draft 8 */ + +const char * +DW_ADDR_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_ADDR_none"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ADDR constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ADDR_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "none"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ADDR constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ADDR_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + default: return 0; + } +} + +/* [7.14] Figure 28 "Identifier case encodings" (p. 144) in DWARFv3 draft 8 */ + +const char * +DW_ID_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_ID_case_sensitive"; + case 0x1: return "DW_ID_up_case"; + case 0x2: return "DW_ID_down_case"; + case 0x3: return "DW_ID_case_insensitive"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ID constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ID_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "case sensitive"; + case 0x1: return "up case"; + case 0x2: return "down case"; + case 0x3: return "case insensitive"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ID constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ID_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.15] Figure 29 "Calling convention encodings" (p. 144) in DWARFv3 draft 8 */ + +const char * +DW_CC_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_CC_normal"; + case 0x02: return "DW_CC_program"; + case 0x03: return "DW_CC_nocall"; + case 0x40: return "DW_CC_lo_user"; + case 0xff: return "DW_CC_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CC constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_CC_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "normal"; + case 0x02: return "program"; + case 0x03: return "nocall"; + case 0x40: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CC constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_CC_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x40: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.16] Figure 30 "Inline encodings" (p. 145) in DWARFv3 draft 8 */ + +const char * +DW_INL_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_INL_not_inlined"; + case 0x1: return "DW_INL_inlined"; + case 0x2: return "DW_INL_declared_not_inlined"; + case 0x3: return "DW_INL_declared_inlined"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_INL constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_INL_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "not inlined"; + case 0x1: return "inlined"; + case 0x2: return "declared not inlined"; + case 0x3: return "declared inlined"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_INL constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_INL_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.17] Figure 31 "Ordering encodings" (p. 145) in DWARFv3 draft 8 */ + +const char * +DW_ORD_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_ORD_row_major"; + case 0x1: return "DW_ORD_col_major"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ORD constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ORD_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "row major"; + case 0x1: return "col major"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ORD constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ORD_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + default: return 0; + } +} + +/* [7.18] Figure 32 "Discriminant descriptor encodings" (p. 146) in DWARFv3 draft 8 */ + +const char * +DW_DSC_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_DSC_label"; + case 0x1: return "DW_DSC_range"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_DSC constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_DSC_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "label"; + case 0x1: return "range"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_DSC constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_DSC_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + default: return 0; + } +} + +/* [7.21] Figure 33 "Line Number Standard Opcode Encodings" (pp. 148-149) in DWARFv3 draft 8 */ + +const char * +DW_LNS_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "DW_LNS_copy"; + case 0x2: return "DW_LNS_advance_pc"; + case 0x3: return "DW_LNS_advance_line"; + case 0x4: return "DW_LNS_set_file"; + case 0x5: return "DW_LNS_set_column"; + case 0x6: return "DW_LNS_negate_stmt"; + case 0x7: return "DW_LNS_set_basic_block"; + case 0x8: return "DW_LNS_const_add_pc"; + case 0x9: return "DW_LNS_fixed_advance_pc"; + case 0xa: return "DW_LNS_set_prologue_end"; + case 0xb: return "DW_LNS_set_epilogue_begin"; + case 0xc: return "DW_LNS_set_isa"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_LNS_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "copy"; + case 0x2: return "advance pc"; + case 0x3: return "advance line"; + case 0x4: return "set file"; + case 0x5: return "set column"; + case 0x6: return "negate stmt"; + case 0x7: return "set basic block"; + case 0x8: return "const add pc"; + case 0x9: return "fixed advance pc"; + case 0xa: return "set prologue end"; + case 0xb: return "set epilogue begin"; + case 0xc: return "set isa"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_LNS_value_to_class (uint32_t val) +{ + switch (val) { + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + case 0x4: return 0; + case 0x5: return 0; + case 0x6: return 0; + case 0x7: return 0; + case 0x8: return 0; + case 0x9: return 0; + case 0xa: return DRC_DWARFv3; + case 0xb: return DRC_DWARFv3; + case 0xc: return DRC_DWARFv3; + default: return 0; + } +} + +/* [7.21] Figure 34 "Line Number Extended Opcode Encodings" (p. 149) in DWARFv3 draft 8 */ + +const char * +DW_LNE_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_LNE_end_sequence"; + case 0x02: return "DW_LNE_set_address"; + case 0x03: return "DW_LNE_define_file"; + case 0x80: return "DW_LNE_lo_user"; + case 0xff: return "DW_LNE_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNE constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_LNE_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "end sequence"; + case 0x02: return "set address"; + case 0x03: return "define file"; + case 0x80: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNE constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_LNE_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x80: return DRC_DWARFv3; + case 0xff: return DRC_DWARFv3; + default: return 0; + } +} + +/* [7.22] Figure 35 "Macinfo Type Encodings" (p. 150) in DWARFv3 draft 8 */ + +const char * +DW_MACINFO_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_MACINFO_define"; + case 0x02: return "DW_MACINFO_undef"; + case 0x03: return "DW_MACINFO_start_file"; + case 0x04: return "DW_MACINFO_end_file"; + case 0xff: return "DW_MACINFO_vendor_ext"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_MACINFO constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_MACINFO_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "define"; + case 0x02: return "undef"; + case 0x03: return "start file"; + case 0x04: return "end file"; + case 0xff: return "vendor ext"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_MACINFO constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_MACINFO_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x04: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.23] Figure 36 "Call frame instruction encodings" (pp. 151-152) in DWARFv3 draft 8 */ + +const char * +DW_CFA_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x40: return "DW_CFA_advance_loc"; + case 0x80: return "DW_CFA_offset"; + case 0xc0: return "DW_CFA_restore"; + case 0x00: return "DW_CFA_nop"; + case 0x01: return "DW_CFA_set_loc"; + case 0x02: return "DW_CFA_advance_loc1"; + case 0x03: return "DW_CFA_advance_loc2"; + case 0x04: return "DW_CFA_advance_loc4"; + case 0x05: return "DW_CFA_offset_extended"; + case 0x06: return "DW_CFA_restore_extended"; + case 0x07: return "DW_CFA_undefined"; + case 0x08: return "DW_CFA_same_value"; + case 0x09: return "DW_CFA_register"; + case 0x0a: return "DW_CFA_remember_state"; + case 0x0b: return "DW_CFA_restore_state"; + case 0x0c: return "DW_CFA_def_cfa"; + case 0x0d: return "DW_CFA_def_cfa_register"; + case 0x0e: return "DW_CFA_def_cfa_offset"; + case 0x0f: return "DW_CFA_def_cfa_expression"; + case 0x10: return "DW_CFA_expression"; + case 0x11: return "DW_CFA_offset_extended_sf"; + case 0x12: return "DW_CFA_def_cfa_sf"; + case 0x13: return "DW_CFA_def_cfa_offset_sf"; + case 0x1c: return "DW_CFA_lo_user"; + case 0x3f: return "DW_CFA_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CFA constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_CFA_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x40: return "advance loc"; + case 0x80: return "offset"; + case 0xc0: return "restore"; + case 0x00: return "nop"; + case 0x01: return "set loc"; + case 0x02: return "advance loc1"; + case 0x03: return "advance loc2"; + case 0x04: return "advance loc4"; + case 0x05: return "offset extended"; + case 0x06: return "restore extended"; + case 0x07: return "undefined"; + case 0x08: return "same value"; + case 0x09: return "register"; + case 0x0a: return "remember state"; + case 0x0b: return "restore state"; + case 0x0c: return "def cfa"; + case 0x0d: return "def cfa register"; + case 0x0e: return "def cfa offset"; + case 0x0f: return "def cfa expression"; + case 0x10: return "expression"; + case 0x11: return "offset extended sf"; + case 0x12: return "def cfa sf"; + case 0x13: return "def cfa offset sf"; + case 0x1c: return "lo user"; + case 0x3f: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CFA constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_CFA_value_to_class (uint32_t val) +{ + switch (val) { + case 0x40: return DRC_ZEROOPERANDS; + case 0x80: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_OFFSET; + case 0xc0: return DRC_ZEROOPERANDS; + case 0x00: return DRC_ZEROOPERANDS; + case 0x01: return DRC_ONEOPERAND | DRC_OPERANDONE_ADDRESS; + case 0x02: return DRC_ONEOPERAND | DRC_OPERANDONE_1BYTE_DELTA; + case 0x03: return DRC_ONEOPERAND | DRC_OPERANDONE_2BYTE_DELTA; + case 0x04: return DRC_ONEOPERAND | DRC_OPERANDONE_4BYTE_DELTA; + case 0x05: return DRC_OPERANDTWO_ULEB128_OFFSET | DRC_OPERNADONE_ULEB128_REGISTER | DRC_TWOOPERANDS; + case 0x06: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x07: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x08: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x09: return DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_ULEB128_REGISTER | DRC_TWOOPERANDS; + case 0x0a: return DRC_ZEROOPERANDS; + case 0x0b: return DRC_ZEROOPERANDS; + case 0x0c: return DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_ULEB128_OFFSET | DRC_TWOOPERANDS; + case 0x0d: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x0e: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_OFFSET; + case 0x0f: return DRC_DWARFv3 | DRC_ONEOPERAND | DRC_OPERANDONE_BLOCK; + case 0x10: return DRC_DWARFv3 | DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_BLOCK | DRC_TWOOPERANDS; + case 0x11: return DRC_DWARFv3 | DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_SLEB128_OFFSET | DRC_TWOOPERANDS; + case 0x12: return DRC_DWARFv3 | DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_SLEB128_OFFSET | DRC_TWOOPERANDS; + case 0x13: return DRC_DWARFv3 | DRC_ONEOPERAND | DRC_OPERANDONE_SLEB128_OFFSET; + case 0x1c: return 0; + case 0x3f: return 0; + default: return 0; + } +} + +/* FSF exception handling Pointer-Encoding constants (CFI augmentation) -- "DW_EH_PE_..." in the FSF sources */ + +const char * +DW_GNU_EH_PE_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x00: return "DW_GNU_EH_PE_absptr"; + case 0x01: return "DW_GNU_EH_PE_uleb128"; + case 0x02: return "DW_GNU_EH_PE_udata2"; + case 0x03: return "DW_GNU_EH_PE_udata4"; + case 0x04: return "DW_GNU_EH_PE_udata8"; + case 0x09: return "DW_GNU_EH_PE_sleb128"; + case 0x0a: return "DW_GNU_EH_PE_sdata2"; + case 0x0b: return "DW_GNU_EH_PE_sdata4"; + case 0x0c: return "DW_GNU_EH_PE_sdata8"; + case 0x08: return "DW_GNU_EH_PE_signed"; + case 0x10: return "DW_GNU_EH_PE_pcrel"; + case 0x20: return "DW_GNU_EH_PE_textrel"; + case 0x30: return "DW_GNU_EH_PE_datarel"; + case 0x40: return "DW_GNU_EH_PE_funcrel"; + case 0x50: return "DW_GNU_EH_PE_aligned"; + case 0x80: return "DW_GNU_EH_PE_indirect"; + case 0xff: return "DW_GNU_EH_PE_omit"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_GNU_EH_PE constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_GNU_EH_PE_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x00: return "absptr"; + case 0x01: return "uleb128"; + case 0x02: return "udata2"; + case 0x03: return "udata4"; + case 0x04: return "udata8"; + case 0x09: return "sleb128"; + case 0x0a: return "sdata2"; + case 0x0b: return "sdata4"; + case 0x0c: return "sdata8"; + case 0x08: return "signed"; + case 0x10: return "pcrel"; + case 0x20: return "textrel"; + case 0x30: return "datarel"; + case 0x40: return "funcrel"; + case 0x50: return "aligned"; + case 0x80: return "indirect"; + case 0xff: return "omit"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_GNU_EH_PE constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_GNU_EH_PE_value_to_class (uint32_t val) +{ + switch (val) { + case 0x00: return DRC_VENDOR_GNU; + case 0x01: return DRC_VENDOR_GNU; + case 0x02: return DRC_VENDOR_GNU; + case 0x03: return DRC_VENDOR_GNU; + case 0x04: return DRC_VENDOR_GNU; + case 0x09: return DRC_VENDOR_GNU; + case 0x0a: return DRC_VENDOR_GNU; + case 0x0b: return DRC_VENDOR_GNU; + case 0x0c: return DRC_VENDOR_GNU; + case 0x08: return DRC_VENDOR_GNU; + case 0x10: return DRC_VENDOR_GNU; + case 0x20: return DRC_VENDOR_GNU; + case 0x30: return DRC_VENDOR_GNU; + case 0x40: return DRC_VENDOR_GNU; + case 0x50: return DRC_VENDOR_GNU; + case 0x80: return DRC_VENDOR_GNU; + case 0xff: return DRC_VENDOR_GNU; + default: return 0; + } +} + +bool +is_type_tag (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_const_type: + case DW_TAG_enumeration_type: + case DW_TAG_file_type: + case DW_TAG_interface_type: + case DW_TAG_packed_type: + case DW_TAG_pointer_type: + case DW_TAG_ptr_to_member_type: + case DW_TAG_reference_type: + case DW_TAG_restrict_type: + case DW_TAG_set_type: + case DW_TAG_shared_type: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_subrange_type: + case DW_TAG_subroutine_type: + case DW_TAG_thrown_type: + case DW_TAG_union_type: + case DW_TAG_unspecified_type: + case DW_TAG_volatile_type: + return true; + default: + return false; + } +} + +bool +is_pubtype_tag (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_file_type: + case DW_TAG_interface_type: + case DW_TAG_set_type: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_subrange_type: + case DW_TAG_subroutine_type: + case DW_TAG_thrown_type: + case DW_TAG_typedef: + case DW_TAG_union_type: + case DW_TAG_unspecified_type: + return true; + default: + break; + } + return false; +} + +DW_TAG_CategoryEnum +get_tag_category (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type : return TagCategoryType; + case DW_TAG_class_type : return TagCategoryType; + case DW_TAG_entry_point : return TagCategoryProgram; + case DW_TAG_enumeration_type : return TagCategoryType; + case DW_TAG_formal_parameter : return TagCategoryVariable; + case DW_TAG_imported_declaration : return TagCategoryProgram; + case DW_TAG_label : return TagCategoryProgram; + case DW_TAG_lexical_block : return TagCategoryProgram; + case DW_TAG_member : return TagCategoryType; + case DW_TAG_pointer_type : return TagCategoryType; + case DW_TAG_reference_type : return TagCategoryType; + case DW_TAG_compile_unit : return TagCategoryProgram; + case DW_TAG_string_type : return TagCategoryType; + case DW_TAG_structure_type : return TagCategoryType; + case DW_TAG_subroutine_type : return TagCategoryType; + case DW_TAG_typedef : return TagCategoryType; + case DW_TAG_union_type : return TagCategoryType; + case DW_TAG_unspecified_parameters : return TagCategoryVariable; + case DW_TAG_variant : return TagCategoryType; + case DW_TAG_common_block : return TagCategoryProgram; + case DW_TAG_common_inclusion : return TagCategoryProgram; + case DW_TAG_inheritance : return TagCategoryType; + case DW_TAG_inlined_subroutine : return TagCategoryProgram; + case DW_TAG_module : return TagCategoryProgram; + case DW_TAG_ptr_to_member_type : return TagCategoryType; + case DW_TAG_set_type : return TagCategoryType; + case DW_TAG_subrange_type : return TagCategoryType; + case DW_TAG_with_stmt : return TagCategoryProgram; + case DW_TAG_access_declaration : return TagCategoryProgram; + case DW_TAG_base_type : return TagCategoryType; + case DW_TAG_catch_block : return TagCategoryProgram; + case DW_TAG_const_type : return TagCategoryType; + case DW_TAG_constant : return TagCategoryVariable; + case DW_TAG_enumerator : return TagCategoryType; + case DW_TAG_file_type : return TagCategoryType; + case DW_TAG_friend : return TagCategoryType; + case DW_TAG_namelist : return TagCategoryVariable; + case DW_TAG_namelist_item : return TagCategoryVariable; + case DW_TAG_packed_type : return TagCategoryType; + case DW_TAG_subprogram : return TagCategoryProgram; + case DW_TAG_template_type_parameter : return TagCategoryType; + case DW_TAG_template_value_parameter : return TagCategoryType; + case DW_TAG_thrown_type : return TagCategoryType; + case DW_TAG_try_block : return TagCategoryProgram; + case DW_TAG_variant_part : return TagCategoryType; + case DW_TAG_variable : return TagCategoryVariable; + case DW_TAG_volatile_type : return TagCategoryType; + case DW_TAG_dwarf_procedure : return TagCategoryProgram; + case DW_TAG_restrict_type : return TagCategoryType; + case DW_TAG_interface_type : return TagCategoryType; + case DW_TAG_namespace : return TagCategoryProgram; + case DW_TAG_imported_module : return TagCategoryProgram; + case DW_TAG_unspecified_type : return TagCategoryType; + case DW_TAG_partial_unit : return TagCategoryProgram; + case DW_TAG_imported_unit : return TagCategoryProgram; + case DW_TAG_shared_type : return TagCategoryType; + default: break; + } + return TagCategoryProgram; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h new file mode 100644 index 000000000000..dafe8a7c8b40 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h @@ -0,0 +1,252 @@ +//===-- DWARFDefines.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFDefines_h_ +#define liblldb_DWARFDefines_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "lldb/Core/dwarf.h" + +/* DWARF constants generated on Wed Sep 7 16:41:50 2005 */ + +typedef uint32_t DRC_class; // Holds DRC_* class bitfields + +/* [7.5.4] Figure 16 "Tag Encodings" (pp. 125-127) in DWARFv3 draft 8 */ + + +enum DW_TAG_Category +{ + TagCategoryVariable, + TagCategoryType, + TagCategoryProgram, + kNumTagCategories +}; + +typedef enum DW_TAG_Category DW_TAG_CategoryEnum; +const char *DW_TAG_value_to_name (uint32_t val); +const char *DW_TAG_value_to_englishy_name (uint32_t val); +DRC_class DW_TAG_value_to_class (uint32_t val); +DW_TAG_CategoryEnum get_tag_category (uint16_t tag); +#define DW_TAG_MAX_NAME_LENGTH 31 + + +/* [7.5.4] Figure 17 "Child determination encodings" (p. 128) in DWARFv3 draft 8 */ + +const char *DW_CHILDREN_value_to_name (uint8_t val); +const char *DW_CHILDREN_value_to_englishy_name (uint8_t val); +DRC_class DW_CHILDREN_value_to_class (uint32_t val); +#define DW_CHILDREN_MAX_NAME_LENGTH 15 + + +/* [7.5.4] Figure 18 "Attribute encodings" (pp. 129-132) in DWARFv3 draft 8 */ + + +const char *DW_AT_value_to_name (uint32_t val); +const char *DW_AT_value_to_englishy_name (uint32_t val); +DRC_class DW_AT_value_to_class (uint32_t val); +#define DW_AT_MAX_NAME_LENGTH 34 + + +/* [7.5.4] Figure 19 "Attribute form encodings" (pp. 133-134) in DWARFv3 draft 8 */ + +const char *DW_FORM_value_to_name (uint32_t val); +const char *DW_FORM_value_to_englishy_name (uint32_t val); +DRC_class DW_FORM_value_to_class (uint32_t val); +#define DW_FORM_MAX_NAME_LENGTH 17 + + +/* [7.7.1] Figure 22 "DWARF operation encodings" (pp. 136-139) in DWARFv3 draft 8 */ + +const char *DW_OP_value_to_name (uint32_t val); +const char *DW_OP_value_to_englishy_name (uint32_t val); +DRC_class DW_OP_value_to_class (uint32_t val); +#define DW_OP_MAX_NAME_LENGTH 25 + + +/* [7.8] Figure 23 "Base type encoding values" (pp. 140-141) in DWARFv3 draft 8 */ + +const char *DW_ATE_value_to_name (uint32_t val); +const char *DW_ATE_value_to_englishy_name (uint32_t val); +DRC_class DW_ATE_value_to_class (uint32_t val); +#define DW_ATE_MAX_NAME_LENGTH 22 + + +/* [7.9] Figure 24 "Accessibility encodings" (p. 141) in DWARFv3 draft 8 */ + +const char *DW_ACCESS_value_to_name (uint32_t val); +const char *DW_ACCESS_value_to_englishy_name (uint32_t val); +DRC_class DW_ACCESS_value_to_class (uint32_t val); +#define DW_ACCESS_MAX_NAME_LENGTH 19 + + +/* [7.10] Figure 25 "Visibility encodings" (p. 142) in DWARFv3 draft 8 */ + +const char *DW_VIS_value_to_name (uint32_t val); +const char *DW_VIS_value_to_englishy_name (uint32_t val); +DRC_class DW_VIS_value_to_class (uint32_t val); +#define DW_VIS_MAX_NAME_LENGTH 16 + + +/* [7.11] Figure 26 "Virtuality encodings" (p. 142) in DWARFv3 draft 8 */ + +const char *DW_VIRTUALITY_value_to_name (uint32_t val); +const char *DW_VIRTUALITY_value_to_englishy_name (uint32_t val); +DRC_class DW_VIRTUALITY_value_to_class (uint32_t val); +#define DW_VIRTUALITY_MAX_NAME_LENGTH 26 + + +/* [7.12] Figure 27 "Language encodings" (p. 143) in DWARFv3 draft 8 */ + +const char *DW_LANG_value_to_name (uint32_t val); +const char *DW_LANG_value_to_englishy_name (uint32_t val); +DRC_class DW_LANG_value_to_class (uint32_t val); +#define DW_LANG_MAX_NAME_LENGTH 19 + + +/* [7.13], "Address Class Encodings" (p. 144) in DWARFv3 draft 8 */ + +const char *DW_ADDR_value_to_name (uint32_t val); +const char *DW_ADDR_value_to_englishy_name (uint32_t val); +DRC_class DW_ADDR_value_to_class (uint32_t val); +#define DW_ADDR_MAX_NAME_LENGTH 12 + + +/* [7.14] Figure 28 "Identifier case encodings" (p. 144) in DWARFv3 draft 8 */ + +const char *DW_ID_value_to_name (uint32_t val); +const char *DW_ID_value_to_englishy_name (uint32_t val); +DRC_class DW_ID_value_to_class (uint32_t val); +#define DW_ID_MAX_NAME_LENGTH 22 + + +/* [7.15] Figure 29 "Calling convention encodings" (p. 144) in DWARFv3 draft 8 */ + +const char *DW_CC_value_to_name (uint32_t val); +const char *DW_CC_value_to_englishy_name (uint32_t val); +DRC_class DW_CC_value_to_class (uint32_t val); +#define DW_CC_MAX_NAME_LENGTH 13 + + +/* [7.16] Figure 30 "Inline encodings" (p. 145) in DWARFv3 draft 8 */ + +const char *DW_INL_value_to_name (uint32_t val); +const char *DW_INL_value_to_englishy_name (uint32_t val); +DRC_class DW_INL_value_to_class (uint32_t val); +#define DW_INL_MAX_NAME_LENGTH 27 + + +/* [7.17] Figure 31 "Ordering encodings" (p. 145) in DWARFv3 draft 8 */ + +const char *DW_ORD_value_to_name (uint32_t val); +const char *DW_ORD_value_to_englishy_name (uint32_t val); +DRC_class DW_ORD_value_to_class (uint32_t val); +#define DW_ORD_MAX_NAME_LENGTH 16 + + +/* [7.18] Figure 32 "Discriminant descriptor encodings" (p. 146) in DWARFv3 draft 8 */ + +const char *DW_DSC_value_to_name (uint32_t val); +const char *DW_DSC_value_to_englishy_name (uint32_t val); +DRC_class DW_DSC_value_to_class (uint32_t val); +#define DW_DSC_MAX_NAME_LENGTH 12 + + +/* [7.21] Figure 33 "Line Number Standard Opcode Encodings" (pp. 148-149) in DWARFv3 draft 8 */ + +const char *DW_LNS_value_to_name (uint32_t val); +const char *DW_LNS_value_to_englishy_name (uint32_t val); +DRC_class DW_LNS_value_to_class (uint32_t val); +#define DW_LNS_MAX_NAME_LENGTH 25 + + +/* [7.21] Figure 34 "Line Number Extended Opcode Encodings" (p. 149) in DWARFv3 draft 8 */ + +const char *DW_LNE_value_to_name (uint32_t val); +const char *DW_LNE_value_to_englishy_name (uint32_t val); +DRC_class DW_LNE_value_to_class (uint32_t val); +#define DW_LNE_MAX_NAME_LENGTH 19 + + +/* [7.22] Figure 35 "Macinfo Type Encodings" (p. 150) in DWARFv3 draft 8 */ + +const char *DW_MACINFO_value_to_name (uint32_t val); +const char *DW_MACINFO_value_to_englishy_name (uint32_t val); +DRC_class DW_MACINFO_value_to_class (uint32_t val); +#define DW_MACINFO_MAX_NAME_LENGTH 21 + + +/* [7.23] Figure 36 "Call frame instruction encodings" (pp. 151-152) in DWARFv3 draft 8 */ + +const char *DW_CFA_value_to_name (uint32_t val); +const char *DW_CFA_value_to_englishy_name (uint32_t val); +DRC_class DW_CFA_value_to_class (uint32_t val); +#define DW_CFA_MAX_NAME_LENGTH 25 + + +/* FSF exception handling Pointer-Encoding constants (CFI augmentation) -- "DW_EH_PE_..." in the FSF sources */ + +const char *DW_GNU_EH_PE_value_to_name (uint32_t val); +const char *DW_GNU_EH_PE_value_to_englishy_name (uint32_t val); +DRC_class DW_GNU_EH_PE_value_to_class (uint32_t val); +#define DW_GNU_EH_PE_MAX_NAME_LENGTH 21 + + +/* These DRC are entirely our own construction, + although they are derived from various comments in the DWARF standard. + Most of these are not useful to the parser, but the DW_AT and DW_FORM + classes should prove to be usable in some fashion. */ + +#define DRC_0x65 0x1 +#define DRC_ADDRESS 0x2 +#define DRC_BLOCK 0x4 +#define DRC_CONSTANT 0x8 +#define DRC_DWARFv3 0x10 +#define DRC_FLAG 0x20 +#define DRC_INDIRECT_SPECIAL 0x40 +#define DRC_LINEPTR 0x80 +#define DRC_LOCEXPR 0x100 +#define DRC_LOCLISTPTR 0x200 +#define DRC_MACPTR 0x400 +#define DRC_ONEOPERAND 0x800 +#define DRC_OPERANDONE_1BYTE_DELTA 0x1000 +#define DRC_OPERANDONE_2BYTE_DELTA 0x2000 +#define DRC_OPERANDONE_4BYTE_DELTA 0x4000 +#define DRC_OPERANDONE_ADDRESS 0x8000 +#define DRC_OPERANDONE_BLOCK 0x10000 +#define DRC_OPERANDONE_SLEB128_OFFSET 0x20000 +#define DRC_OPERANDONE_ULEB128_OFFSET 0x40000 +#define DRC_OPERANDONE_ULEB128_REGISTER 0x80000 +#define DRC_OPERANDTWO_BLOCK 0x100000 +#define DRC_OPERANDTWO_SLEB128_OFFSET 0x200000 +#define DRC_OPERANDTWO_ULEB128_OFFSET 0x400000 +#define DRC_OPERANDTWO_ULEB128_REGISTER 0x800000 +#define DRC_OPERNADONE_ULEB128_REGISTER 0x1000000 +#define DRC_RANGELISTPTR 0x2000000 +#define DRC_REFERENCE 0x4000000 +#define DRC_STRING 0x8000000 +#define DRC_TWOOPERANDS 0x10000000 +#define DRC_VENDOR_GNU 0x20000000 +#define DRC_VENDOR_MIPS 0x40000000 +#define DRC_ZEROOPERANDS 0x80000000 + +bool is_type_tag (uint16_t tag); +bool is_pubtype_tag (uint16_t tag); + + +#ifdef __cplusplus +} +#endif + + +#endif // liblldb_DWARFDefines_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp new file mode 100644 index 000000000000..d2c137bd7df8 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -0,0 +1,571 @@ +//===-- DWARFFormValue.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/Core/dwarf.h" +#include "lldb/Core/Stream.h" + +#include "DWARFFormValue.h" +#include "DWARFCompileUnit.h" + +class DWARFCompileUnit; + +using namespace lldb_private; + +DWARFFormValue::DWARFFormValue(dw_form_t form) : + m_form(form), + m_value() +{ +} + +bool +DWARFFormValue::ExtractValue(const DataExtractor& data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) +{ + bool indirect = false; + bool is_block = false; + m_value.data = NULL; + // Read the value for the form into value and follow and DW_FORM_indirect instances we run into + do + { + indirect = false; + switch (m_form) + { + case DW_FORM_addr: m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); break; + case DW_FORM_block2: m_value.value.uval = data.GetU16(offset_ptr); is_block = true; break; + case DW_FORM_block4: m_value.value.uval = data.GetU32(offset_ptr); is_block = true; break; + case DW_FORM_data2: m_value.value.uval = data.GetU16(offset_ptr); break; + case DW_FORM_data4: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_data8: m_value.value.uval = data.GetU64(offset_ptr); break; + case DW_FORM_string: m_value.value.cstr = data.GetCStr(offset_ptr); + // Set the string value to also be the data for inlined cstr form values only + // so we can tell the differnence between DW_FORM_string and DW_FORM_strp form + // values; + m_value.data = (uint8_t*)m_value.value.cstr; break; + case DW_FORM_block: m_value.value.uval = data.GetULEB128(offset_ptr); is_block = true; break; + case DW_FORM_block1: m_value.value.uval = data.GetU8(offset_ptr); is_block = true; break; + case DW_FORM_data1: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_flag: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_sdata: m_value.value.sval = data.GetSLEB128(offset_ptr); break; + case DW_FORM_strp: m_value.value.uval = data.GetU32(offset_ptr); break; + // case DW_FORM_APPLE_db_str: + case DW_FORM_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_ref_addr: m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); break; + case DW_FORM_ref1: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_ref2: m_value.value.uval = data.GetU16(offset_ptr); break; + case DW_FORM_ref4: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_ref8: m_value.value.uval = data.GetU64(offset_ptr); break; + case DW_FORM_ref_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_indirect: + m_form = data.GetULEB128(offset_ptr); + indirect = true; + break; + + default: + return false; + break; + } + } while (indirect); + + if (is_block) + { + m_value.data = data.PeekData(*offset_ptr, m_value.value.uval); + if (m_value.data != NULL) + { + *offset_ptr += m_value.value.uval; + } + } + + return true; +} + +bool +DWARFFormValue::SkipValue(const DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) const +{ + return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, cu); +} + +bool +DWARFFormValue::SkipValue(dw_form_t form, const DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) +{ + bool indirect = false; + do + { + indirect = false; + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_block : { dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block1 : { dw_uleb128_t size = debug_info_data.GetU8(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block2 : { dw_uleb128_t size = debug_info_data.GetU16(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block4 : { dw_uleb128_t size = debug_info_data.GetU32(offset_ptr); *offset_ptr += size; } return true; + + // Inlined NULL terminated C-strings + case DW_FORM_string : + debug_info_data.GetCStr(offset_ptr); + return true; + + // Compile unit address sized values + case DW_FORM_addr : + case DW_FORM_ref_addr : + *offset_ptr += DWARFCompileUnit::GetAddressByteSize(cu); + return true; + + // 1 byte values + case DW_FORM_data1 : + case DW_FORM_flag : + case DW_FORM_ref1 : + *offset_ptr += 1; + return true; + + // 2 byte values + case DW_FORM_data2 : + case DW_FORM_ref2 : + *offset_ptr += 2; + return true; + + // 4 byte values + case DW_FORM_strp : + case DW_FORM_data4 : + case DW_FORM_ref4 : + *offset_ptr += 4; + return true; + + // 8 byte values + case DW_FORM_data8 : + case DW_FORM_ref8 : + *offset_ptr += 8; + return true; + + // signed or unsigned LEB 128 values + // case DW_FORM_APPLE_db_str: + case DW_FORM_sdata : + case DW_FORM_udata : + case DW_FORM_ref_udata : + debug_info_data.Skip_LEB128(offset_ptr); + return true; + + case DW_FORM_indirect : + indirect = true; + form = debug_info_data.GetULEB128(offset_ptr); + break; + default: + return false; + } + } while (indirect); + return true; +} + +//bool +//DWARFFormValue::PutUnsigned(dw_form_t form, dw_offset_t offset, uint64_t value, BinaryStreamBuf& out_buff, const DWARFCompileUnit* cu, bool fixup_cu_relative_refs) +//{ +// assert(offset != DW_INVALID_OFFSET); +//// printf("PutUnsigned(%s, 0x%8.8x, 0x%16.16llx, %d)\n", DW_FORM_value_to_name(form), offset, value, fixup_cu_relative_refs); +// // Read the value for the form into value and follow and DW_FORM_indirect instances we run into +// switch (form) +// { +// case DW_FORM_addr: offset = out_buff.PutMax64(offset, value, DWARFCompileUnit::GetAddressByteSize(cu)); break; +// +// case DW_FORM_flag: +// case DW_FORM_data1: offset = out_buff.Put8(offset, value); break; +// case DW_FORM_data2: offset = out_buff.Put16(offset, value); break; +// case DW_FORM_data4: offset = out_buff.Put32(offset, value); break; +// case DW_FORM_data8: offset = out_buff.Put64(offset, value); break; +//// case DW_FORM_udata: offset = out_buff.Put32_as_ULEB128(offset, value); break; +//// case DW_FORM_sdata: offset = out_buff.Put32_as_SLEB128(offset, value); break; +// case DW_FORM_strp: offset = out_buff.Put32(offset, value); break; +//// case DW_FORM_APPLE_db_str: +//// offset = out_buff.Put32_as_ULEB128(offset, value); break; +// +// case DW_FORM_ref1: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put8(offset, value); +// break; +// case DW_FORM_ref2: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put16(offset, value); +// break; +// case DW_FORM_ref4: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put32(offset, value); +// break; +// case DW_FORM_ref8: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put64(offset, value); +// break; +//// case DW_FORM_ref_udata: +//// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +//// offset = out_buff.Put32_as_ULEB128(offset, value); +//// break; +// case DW_FORM_ref_addr: +// // TODO: Add support for DWARF3 if we ever start emitting DWARF3. The DW_FORM_ref_addr +// // is always the same size as an address prior to DWARF3, and with DWARF3 or later it +// // is 4 hard coded to bytes. +// offset = out_buff.PutMax64(offset, value, DWARFCompileUnit::GetAddressByteSize(cu)); +// break; +// +// default: +// return false; +// } +// +// return true; +//} + +//bool +//DWARFFormValue::TransferValue(dw_form_t form, const DataExtractor& data, uint32_t* offset_ptr, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff) +//{ +// DWARFFormValue formValue(form); +// if (formValue.ExtractValue(data, offset_ptr,cu)) +// return TransferValue(formValue, cu, out_buff); +// return false; +//} + +//bool +//DWARFFormValue::TransferValue(const DWARFFormValue& formValue, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff) +//{ +// // Read the value for the form into value and follow and DW_FORM_indirect instances we run into +// dw_form_t form = formValue.Form(); +// switch (form) +// { +// case DW_FORM_addr: +// case DW_FORM_ref_addr: +// { +// uint8_t addr_size = DWARFCompileUnit::GetAddressByteSize(cu); +// out_buff.AppendMax64(formValue.Unsigned(), addr_size); +// } +// break; +// +// case DW_FORM_block: out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// case DW_FORM_block1: out_buff.Append8(formValue.Unsigned()); break; +// case DW_FORM_block2: out_buff.Append16(formValue.Unsigned()); break; +// case DW_FORM_block4: out_buff.Append32(formValue.Unsigned()); break; +// +// case DW_FORM_flag: +// case DW_FORM_data1: out_buff.Append8(formValue.Unsigned()); break; +// case DW_FORM_data2: out_buff.Append16(formValue.Unsigned()); break; +// case DW_FORM_data4: out_buff.Append32(formValue.Unsigned()); break; +// case DW_FORM_data8: out_buff.Append64(formValue.Unsigned()); break; +// case DW_FORM_udata: out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// case DW_FORM_sdata: out_buff.Append32_as_SLEB128(formValue.Signed()); break; +// +// case DW_FORM_string: out_buff.AppendCStr(formValue.m_value.value.cstr); break; +// case DW_FORM_strp: out_buff.Append32(formValue.Unsigned()); break; +//// case DW_FORM_APPLE_db_str: +//// out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// +// case DW_FORM_ref1: out_buff.Append8(formValue.Unsigned()); break; +// case DW_FORM_ref2: out_buff.Append16(formValue.Unsigned()); break; +// case DW_FORM_ref4: out_buff.Append32(formValue.Unsigned()); break; +// case DW_FORM_ref8: out_buff.Append64(formValue.Unsigned()); break; +// case DW_FORM_ref_udata: out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// +// case DW_FORM_indirect: +// assert(!"DW_FORM_indirect found in DWARFFormValue::TransferValue() for an extracted form..."); +// break; +// +// default: +// Log::Error("DWARFFormValue::TransferValue() Unrecognized form: 0x%4.4x", form); +// return false; +// break; +// } +// +// const uint8_t* block_data = formValue.BlockData(); +// if (block_data) +// out_buff.AppendData(block_data, formValue.Unsigned()); +// return true; +//} + +void +DWARFFormValue::Dump(Stream *s, const DataExtractor* debug_str_data, const DWARFCompileUnit* cu) const +{ + uint64_t uvalue = Unsigned(); + bool cu_relative_offset = false; + + bool verbose = s->GetVerbose(); + + switch (m_form) + { + case DW_FORM_addr: s->Address(uvalue, sizeof (uint64_t)); break; + case DW_FORM_flag: + case DW_FORM_data1: s->PutHex8(uvalue); break; + case DW_FORM_data2: s->PutHex16(uvalue); break; + case DW_FORM_data4: s->PutHex32(uvalue); break; + case DW_FORM_data8: s->PutHex64(uvalue); break; + case DW_FORM_string: s->QuotedCString(AsCString(NULL)); break; + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (uvalue > 0) + { + switch (m_form) + { + case DW_FORM_block: s->Printf("<0x%llx> ", uvalue); break; + case DW_FORM_block1: s->Printf("<0x%2.2x> ", (uint8_t)uvalue); break; + case DW_FORM_block2: s->Printf("<0x%4.4x> ", (uint16_t)uvalue); break; + case DW_FORM_block4: s->Printf("<0x%8.8x> ", (uint32_t)uvalue); break; + default: break; + } + + const uint8_t* data_ptr = m_value.data; + if (data_ptr) + { + const uint8_t* end_data_ptr = data_ptr + uvalue; // uvalue contains size of block + while (data_ptr < end_data_ptr) + { + s->Printf("%2.2x ", *data_ptr); + ++data_ptr; + } + } + else + s->PutCString("NULL"); + } + break; + + case DW_FORM_sdata: s->PutSLEB128(uvalue); break; + case DW_FORM_udata: s->PutULEB128(uvalue); break; + case DW_FORM_strp: + if (debug_str_data) + { + if (verbose) + s->Printf(" .debug_str[0x%8.8x] = ", (uint32_t)uvalue); + + const char* dbg_str = AsCString(debug_str_data); + if (dbg_str) + s->QuotedCString(dbg_str); + } + else + { + s->PutHex32(uvalue); + } + break; + + case DW_FORM_ref_addr: + { + s->Address(uvalue, sizeof (uint64_t) * 2); + break; + } + case DW_FORM_ref1: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%2.2x", (uint8_t)uvalue); break; + case DW_FORM_ref2: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%4.4x", (uint16_t)uvalue); break; + case DW_FORM_ref4: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%4.4x", (uint32_t)uvalue); break; + case DW_FORM_ref8: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%8.8llx", uvalue); break; + case DW_FORM_ref_udata: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%llx", uvalue); break; + + // All DW_FORM_indirect attributes should be resolved prior to calling this function + case DW_FORM_indirect: s->PutCString("DW_FORM_indirect"); break; + default: + s->Printf("DW_FORM(0x%4.4x)", m_form); + break; + } + + if (cu_relative_offset) + { + if (verbose) + s->PutCString(" => "); + + s->Printf("{0x%8.8x}", (uvalue + (cu ? cu->GetOffset() : 0))); + } +} + +const char* +DWARFFormValue::AsCString(const DataExtractor* debug_str_data_ptr) const +{ + if (IsInlinedCStr()) + return m_value.value.cstr; + else if (debug_str_data_ptr) + return debug_str_data_ptr->PeekCStr(m_value.value.uval); + return NULL; +} + +uint64_t +DWARFFormValue::Reference(const DWARFCompileUnit* cu) const +{ + uint64_t die_offset = m_value.value.uval; + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + die_offset += (cu ? cu->GetOffset() : 0); + break; + + default: + break; + } + + return die_offset; +} + +//---------------------------------------------------------------------- +// Resolve any compile unit specific references so that we don't need +// the compile unit at a later time in order to work with the form +// value. +//---------------------------------------------------------------------- +bool +DWARFFormValue::ResolveCompileUnitReferences(const DWARFCompileUnit* cu) +{ + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + m_value.value.uval += cu->GetOffset(); + m_form = DW_FORM_ref_addr; + return true; + break; + + default: + break; + } + + return false; +} + +const uint8_t* +DWARFFormValue::BlockData() const +{ + if (!IsInlinedCStr()) + return m_value.data; + return NULL; +} + + +bool +DWARFFormValue::IsBlockForm(const dw_form_t form) +{ + switch (form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + return true; + } + return false; +} + +bool +DWARFFormValue::IsDataForm(const dw_form_t form) +{ + switch (form) + { + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + return true; + } + return false; +} + +int +DWARFFormValue::Compare (const DWARFFormValue& a_value, const DWARFFormValue& b_value, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const DataExtractor* debug_str_data_ptr) +{ + dw_form_t a_form = a_value.Form(); + dw_form_t b_form = b_value.Form(); + if (a_form < b_form) + return -1; + if (a_form > b_form) + return 1; + switch (a_form) + { + case DW_FORM_addr: + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + case DW_FORM_ref_addr: + { + uint64_t a = a_value.Unsigned(); + uint64_t b = b_value.Unsigned(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_sdata: + { + int64_t a = a_value.Signed(); + int64_t b = b_value.Signed(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_string: + case DW_FORM_strp: + { + const char *a_string = a_value.AsCString(debug_str_data_ptr); + const char *b_string = b_value.AsCString(debug_str_data_ptr); + if (a_string == b_string) + return 0; + else if (a_string && b_string) + return strcmp(a_string, b_string); + else if (a_string == NULL) + return -1; // A string is NULL, and B is valid + else + return 1; // A string valid, and B is NULL + } + + + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + { + uint64_t a_len = a_value.Unsigned(); + uint64_t b_len = b_value.Unsigned(); + if (a_len < b_len) + return -1; + if (a_len > b_len) + return 1; + // The block lengths are the same + return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned()); + } + break; + + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + { + uint64_t a = a_value.Reference(a_cu); + uint64_t b = b_value.Reference(b_cu); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_indirect: + assert(!"This shouldn't happen after the form has been extracted..."); + break; + + default: + assert(!"Unhandled DW_FORM"); + break; + } + return -1; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h new file mode 100644 index 000000000000..3db63664f385 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -0,0 +1,81 @@ +//===-- DWARFFormValue.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// + +// +//===----------------------------------------------------------------------===// + + #ifndef liblldb_DWARFFormValue_h_ + #define SymbolFileDWARF_DWARFFormValue_h_ + +#include "SymbolFileDWARF.h" +#include // for NULL + +class DWARFFormValue +{ +public: + typedef struct ValueTypeTag + { + ValueTypeTag() : + data(NULL), + value() + { + value.uval = 0; + } + + union + { + uint64_t uval; + int64_t sval; + const char* cstr; + } value; + const uint8_t* data; + } ValueType; + + enum + { + eValueTypeInvalid = 0, + eValueTypeUnsigned, + eValueTypeSigned, + eValueTypeCStr, + eValueTypeBlock + }; + + DWARFFormValue(dw_form_t form = 0); + dw_form_t Form() const { return m_form; } + void SetForm(dw_form_t form) { m_form = form; } + const ValueType& Value() const { return m_value; } + void Dump(lldb_private::Stream *s, const lldb_private::DataExtractor* debug_str_data, const DWARFCompileUnit* cu) const; + bool ExtractValue(const lldb_private::DataExtractor& data, uint32_t* offset_ptr, const DWARFCompileUnit* cu); + bool IsInlinedCStr() const { return (m_value.data != NULL) && m_value.data == (uint8_t*)m_value.value.cstr; } + const uint8_t* BlockData() const; + uint64_t Reference(const DWARFCompileUnit* cu) const; + bool ResolveCompileUnitReferences(const DWARFCompileUnit* cu); + uint64_t Unsigned() const { return m_value.value.uval; } + void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; } + int64_t Signed() const { return m_value.value.sval; } + void SetSigned(int64_t sval) { m_value.value.sval = sval; } + const char* AsCString(const lldb_private::DataExtractor* debug_str_data_ptr) const; + bool SkipValue(const lldb_private::DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) const; + static bool SkipValue(const dw_form_t form, const lldb_private::DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu); +// static bool TransferValue(dw_form_t form, const lldb_private::DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); +// static bool TransferValue(const DWARFFormValue& formValue, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); +// static bool PutUnsigned(dw_form_t form, dw_offset_t offset, uint64_t value, BinaryStreamBuf& out_buff, const DWARFCompileUnit* cu, bool fixup_cu_relative_refs); + static bool IsBlockForm(const dw_form_t form); + static bool IsDataForm(const dw_form_t form); + + static int Compare (const DWARFFormValue& a, const DWARFFormValue& b, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const lldb_private::DataExtractor* debug_str_data_ptr); +protected: + dw_form_t m_form; // Form for this value + ValueType m_value; // Contains all data for the form +}; + + +#endif // SymbolFileDWARF_DWARFFormValue_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp new file mode 100644 index 000000000000..9229c2a2d22c --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp @@ -0,0 +1,172 @@ +//===-- DWARFLocationDescription.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationDescription.h" +#include "DWARFDefines.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Stream.h" + + +using namespace lldb_private; + +static int print_dwarf_exp_op (Stream *s, const DataExtractor& data, uint32_t* offset_ptr, int address_size, int dwarf_ref_size); + +int +print_dwarf_expression (Stream *s, + const DataExtractor& data, + int address_size, + int dwarf_ref_size, + bool location_expression) +{ + int op_count = 0; + uint32_t offset = 0; + while (data.ValidOffset(offset)) + { + if (location_expression && op_count > 0) + { + // err (baton, "Dwarf location expressions may only have one operand!"); + return 1; + } + if (op_count > 0) + { + s->PutCString(", "); + } + if (print_dwarf_exp_op (s, data, &offset, address_size, dwarf_ref_size) == 1) + return 1; + op_count++; + } + + return 0; +} + +static int +print_dwarf_exp_op (Stream *s, + const DataExtractor& data, + uint32_t* offset_ptr, + int address_size, + int dwarf_ref_size) +{ + uint8_t opcode = data.GetU8(offset_ptr); + DRC_class opcode_class; + uint64_t uint; + int64_t sint; + + int size; + + opcode_class = DW_OP_value_to_class (opcode) & (~DRC_DWARFv3); + + s->Printf("%s ", DW_OP_value_to_englishy_name (opcode)); + + /* Does this take zero parameters? If so we can shortcut this function. */ + if (opcode_class == DRC_ZEROOPERANDS) + return 0; + + if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) + { + uint = data.GetULEB128(offset_ptr); + sint = data.GetSLEB128(offset_ptr); + s->Printf("%llu %lli", uint, sint); + return 0; + } + if (opcode_class != DRC_ONEOPERAND) + { + s->Printf("UNKNOWN OP %u", opcode); + return 1; + } + + switch (opcode) + { + case DW_OP_addr: size = address_size; break; + case DW_OP_const1u: size = 1; break; + case DW_OP_const1s: size = -1; break; + case DW_OP_const2u: size = 2; break; + case DW_OP_const2s: size = -2; break; + case DW_OP_const4u: size = 4; break; + case DW_OP_const4s: size = -4; break; + case DW_OP_const8u: size = 8; break; + case DW_OP_const8s: size = -8; break; + case DW_OP_constu: size = 128; break; + case DW_OP_consts: size = -128; break; + case DW_OP_fbreg: size = -128; break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + size = -128; break; + case DW_OP_pick: + size = 1; break; + case DW_OP_deref_size: + size = 1; break; + case DW_OP_xderef_size: + size = 1; break; + case DW_OP_plus_uconst: + size = 128; break; + case DW_OP_skip: + size = -2; break; + case DW_OP_bra: + size = -2; break; + case DW_OP_call2: + size = 2; break; + case DW_OP_call4: + size = 4; break; + case DW_OP_call_ref: + size = dwarf_ref_size; break; + case DW_OP_piece: + size = 128; break; + case DW_OP_regx: + size = 128; break; + default: + s->Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode); + return 1; + } + + switch (size) + { + case -1: sint = (int8_t) data.GetU8(offset_ptr); s->Printf("%+lli", sint); break; + case -2: sint = (int16_t) data.GetU16(offset_ptr); s->Printf("%+lli", sint); break; + case -4: sint = (int32_t) data.GetU32(offset_ptr); s->Printf("%+lli", sint); break; + case -8: sint = (int64_t) data.GetU64(offset_ptr); s->Printf("%+lli", sint); break; + case -128: sint = data.GetSLEB128(offset_ptr); s->Printf("%+lli", sint); break; + case 1: uint = data.GetU8(offset_ptr); s->Printf("0x%2.2llx", uint); break; + case 2: uint = data.GetU16(offset_ptr); s->Printf("0x%4.4llx", uint); break; + case 4: uint = data.GetU32(offset_ptr); s->Printf("0x%8.8llx", uint); break; + case 8: uint = data.GetU64(offset_ptr); s->Printf("0x%16.16llx", uint); break; + case 128: uint = data.GetULEB128(offset_ptr); s->Printf("0x%llx", uint); break; + } + + return 0; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h new file mode 100644 index 000000000000..413a95c0f499 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h @@ -0,0 +1,24 @@ +//===-- DWARFLocationDescription.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFLocationDescription_h_ +#define SymbolFileDWARF_DWARFLocationDescription_h_ + +#include "SymbolFileDWARF.h" + +int +print_dwarf_expression (lldb_private::Stream *s, + const lldb_private::DataExtractor& data, + int address_size, + int dwarf_ref_size, + bool location_expression); + + + +#endif // SymbolFileDWARF_DWARFLocationDescription_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp new file mode 100644 index 000000000000..6a8359d39865 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp @@ -0,0 +1,89 @@ +//===-- DWARFLocationList.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationList.h" + +#include "lldb/Core/Stream.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugInfo.h" +#include "DWARFLocationDescription.h" + +using namespace lldb_private; + +dw_offset_t +DWARFLocationList::Dump(Stream *s, const DWARFCompileUnit* cu, const DataExtractor& debug_loc_data, dw_offset_t offset) +{ + uint64_t start_addr, end_addr; + uint32_t addr_size = DWARFCompileUnit::GetAddressByteSize(cu); + s->SetAddressByteSize(DWARFCompileUnit::GetAddressByteSize(cu)); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + while (debug_loc_data.ValidOffset(offset)) + { + start_addr = debug_loc_data.GetMaxU64(&offset,addr_size); + end_addr = debug_loc_data.GetMaxU64(&offset,addr_size); + + if (start_addr == 0 && end_addr == 0) + break; + + s->PutCString("\n "); + s->Indent(); + s->AddressRange(start_addr + base_addr, end_addr + base_addr, NULL, ": "); + uint32_t loc_length = debug_loc_data.GetU16(&offset); + + DataExtractor locationData(debug_loc_data, offset, loc_length); + // if ( dump_flags & DWARFDebugInfo::eDumpFlag_Verbose ) *ostrm_ptr << " ( "; + print_dwarf_expression (s, locationData, addr_size, 4, false); + offset += loc_length; + } + + return offset; +} + +bool +DWARFLocationList::Extract(const DataExtractor& debug_loc_data, dw_offset_t* offset_ptr, DataExtractor& location_list_data) +{ + // Initialize with no data just in case we don't find anything + location_list_data.Clear(); + + size_t loc_list_length = Size(debug_loc_data, *offset_ptr); + if (loc_list_length > 0) + { + location_list_data.SetData(debug_loc_data, *offset_ptr, loc_list_length); + *offset_ptr += loc_list_length; + return true; + } + + return false; +} + +size_t +DWARFLocationList::Size(const DataExtractor& debug_loc_data, dw_offset_t offset) +{ + const dw_offset_t debug_loc_offset = offset; + + while (debug_loc_data.ValidOffset(offset)) + { + dw_addr_t start_addr = debug_loc_data.GetAddress(&offset); + dw_addr_t end_addr = debug_loc_data.GetAddress(&offset); + + if (start_addr == 0 && end_addr == 0) + break; + + uint16_t loc_length = debug_loc_data.GetU16(&offset); + offset += loc_length; + } + + if (offset > debug_loc_offset) + return offset - debug_loc_offset; + return 0; +} + + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h new file mode 100644 index 000000000000..3efd5c4492bb --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h @@ -0,0 +1,34 @@ +//===-- DWARFLocationList.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFLocationList_h_ +#define SymbolFileDWARF_DWARFLocationList_h_ + +#include "SymbolFileDWARF.h" + +class DWARFLocationList +{ +public: + static dw_offset_t + Dump (lldb_private::Stream *s, + const DWARFCompileUnit* cu, + const lldb_private::DataExtractor& debug_loc_data, + dw_offset_t offset); + + static bool + Extract (const lldb_private::DataExtractor& debug_loc_data, + dw_offset_t* offset_ptr, + lldb_private::DataExtractor& location_list_data); + + static size_t + Size (const lldb_private::DataExtractor& debug_loc_data, + dw_offset_t offset); + +}; +#endif // SymbolFileDWARF_DWARFLocationList_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp new file mode 100644 index 000000000000..571271b34bb2 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp @@ -0,0 +1,207 @@ +//===-- LogChannelDWARF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LogChannelDWARF.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; + + +// when the one and only logging channel is abled, then this will be non NULL. +static LogChannelDWARF* g_log_channel = NULL; + +LogChannelDWARF::LogChannelDWARF () : + LogChannel () +{ +} + +LogChannelDWARF::~LogChannelDWARF () +{ +} + + +void +LogChannelDWARF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + LogChannelDWARF::CreateInstance); +} + +void +LogChannelDWARF::Terminate() +{ + PluginManager::UnregisterPlugin (LogChannelDWARF::CreateInstance); +} + +LogChannel* +LogChannelDWARF::CreateInstance () +{ + return new LogChannelDWARF (); +} + +const char * +LogChannelDWARF::GetPluginNameStatic() +{ + static std::string g_plugin_name; + if (g_plugin_name.empty()) + { + g_plugin_name = SymbolFileDWARF::GetPluginNameStatic(); + g_plugin_name += LogChannel::GetPluginSuffix (); + } + return g_plugin_name.c_str(); +} + + +const char * +LogChannelDWARF::GetPluginDescriptionStatic() +{ + return "DWARF log channel for debugging plug-in issues."; +} + +const char * +LogChannelDWARF::GetPluginName() +{ + return GetPluginDescriptionStatic(); +} + +const char * +LogChannelDWARF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +LogChannelDWARF::GetPluginVersion() +{ + return 1; +} + + +void +LogChannelDWARF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + + +Error +LogChannelDWARF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorStringWithFormat("No commands are supported.\n"); + return error; +} + + +Log * +LogChannelDWARF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + +void +LogChannelDWARF::Disable () +{ + g_log_channel = NULL; + m_log_sp.reset(); +} + +bool +LogChannelDWARF::Enable +( + StreamSP &log_stream_sp, + uint32_t log_options, + Stream *feedback_strm, // Feedback stream for argument errors etc + const Args &categories // The categories to enable within this logging stream, if empty, enable default set +) +{ + Disable (); + + m_log_sp.reset(new Log (log_stream_sp)); + g_log_channel = this; + uint32_t flag_bits = 0; + bool got_unknown_category = false; + const size_t argc = categories.GetArgumentCount(); + for (size_t i=0; iPrintf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = DWARF_LOG_DEFAULT; + m_log_sp->GetMask().SetAllFlagBits(flag_bits); + m_log_sp->GetOptions().SetAllFlagBits(log_options); + return m_log_sp.get() != NULL; +} + +void +LogChannelDWARF::ListCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " info - log the parsing if .debug_info\n" + " line - log the parsing if .debug_line\n" + " pubnames - log the parsing if .debug_pubnames\n" + " pubtypes - log the parsing if .debug_pubtypes\n\n", + SymbolFileDWARF::GetPluginNameStatic()); +} + +Log * +LogChannelDWARF::GetLog () +{ + if (g_log_channel) + return g_log_channel->m_log_sp.get(); + else + return NULL; +} + +Log * +LogChannelDWARF::GetLogIfAll (uint32_t mask) +{ + Log *log = GetLog(); + if (log) + if (log->GetMask().IsSet(mask)) + return log; + return NULL; +} + + +void +LogChannelDWARF::LogIf (uint32_t mask, const char *format, ...) +{ + if (g_log_channel) + { + LogSP log_sp(g_log_channel->m_log_sp); + va_list args; + va_start (args, format); + log_sp->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h new file mode 100644 index 000000000000..943d1da194f6 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h @@ -0,0 +1,91 @@ +//===-- LogChannelDWARF.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LogChannelDWARF_h_ +#define liblldb_LogChannelDWARF_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define DWARF_LOG_VERBOSE (1u << 0) +#define DWARF_LOG_DEBUG_INFO (1u << 1) +#define DWARF_LOG_DEBUG_LINE (1u << 2) +#define DWARF_LOG_DEBUG_PUBNAMES (1u << 3) +#define DWARF_LOG_DEBUG_PUBTYPES (1u << 4) +#define DWARF_LOG_ALL (UINT32_MAX) +#define DWARF_LOG_DEFAULT (DWARF_LOG_DEBUG_INFO) + +class LogChannelDWARF : public lldb_private::LogChannel +{ +public: + LogChannelDWARF (); + + virtual + ~LogChannelDWARF (); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::LogChannel * + CreateInstance (); + + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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); + + virtual void + Disable (); + + virtual bool + Enable (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + lldb_private::Stream *feedback_strm, // Feedback stream for argument errors etc + const lldb_private::Args &categories); // The categories to enable within this logging stream, if empty, enable default set + + virtual void + ListCategories (lldb_private::Stream *strm); + + static lldb_private::Log * + GetLog (); + + static lldb_private::Log * + GetLogIfAll (uint32_t mask); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_LogChannelDWARF_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp new file mode 100644 index 000000000000..ffee695d3abf --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -0,0 +1,3615 @@ +//===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARF.h" + +// Other libraries and framework includes +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Specifiers.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Value.h" + +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFDebugLine.h" +#include "DWARFDebugPubnames.h" +#include "DWARFDebugRanges.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFLocationList.h" +#include "LogChannelDWARF.h" + +#include + +#define DIE_IS_BEING_PARSED ((void*)1) + +using namespace lldb; +using namespace lldb_private; + + +static const ConstString& +GetSectionNameDebugInfo() +{ + static const ConstString g_sect_name("__debug_info"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugAbbrev() +{ + static const ConstString g_sect_name ("__debug_abbrev"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugAranges() +{ + static const ConstString g_sect_name ("__debug_aranges"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugFrame() +{ + static const ConstString g_sect_name ("__debug_frame"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugLine() +{ + static const ConstString g_sect_name ("__debug_line"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugLoc() +{ + static const ConstString g_sect_name ("__debug_loc"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugMacInfo() +{ + static const ConstString g_sect_name ("__debug_macinfo"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugPubNames() +{ + static const ConstString g_sect_name ("__debug_pubnames"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugPubTypes() +{ + static const ConstString g_sect_name ("__debug_pubtypes"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugRanges() +{ + static const ConstString g_sect_name ("__debug_ranges"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugStr() +{ + static const ConstString g_sect_name ("__debug_str"); + return g_sect_name; +} + +static uint32_t +DwarfToClangAccessibility (uint32_t dwarf_accessibility) +{ + switch (dwarf_accessibility) + { + case DW_ACCESS_public: + return clang::AS_public; + case DW_ACCESS_private: + return clang::AS_private; + case DW_ACCESS_protected: + return clang::AS_protected; + default: + return clang::AS_none; + } +} + +void +SymbolFileDWARF::Initialize() +{ + LogChannelDWARF::Initialize(); + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileDWARF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); + LogChannelDWARF::Initialize(); +} + + +const char * +SymbolFileDWARF::GetPluginNameStatic() +{ + return "symbol-file.dwarf2"; +} + +const char * +SymbolFileDWARF::GetPluginDescriptionStatic() +{ + return "DWARF and DWARF3 debug symbol file reader."; +} + + +SymbolFile* +SymbolFileDWARF::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileDWARF(obj_file); +} + +//---------------------------------------------------------------------- +// Gets the first parent that is a lexical block, function or inlined +// subroutine, or compile unit. +//---------------------------------------------------------------------- +static const DWARFDebugInfoEntry * +GetParentSymbolContextDIE(const DWARFDebugInfoEntry *child_die) +{ + const DWARFDebugInfoEntry *die; + for (die = child_die->GetParent(); die != NULL; die = die->GetParent()) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_compile_unit: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + return die; + } + } + return NULL; +} + + +SymbolFileDWARF::SymbolFileDWARF(ObjectFile* ofile) : + SymbolFile(ofile), + m_flags(), + m_data_debug_abbrev(), + m_data_debug_aranges(), + m_data_debug_frame(), + m_data_debug_info(), + m_data_debug_line(), + m_data_debug_loc(), + m_data_debug_macinfo(), + m_data_debug_pubnames(), + m_data_debug_pubtypes(), + m_data_debug_ranges(), + m_data_debug_str(), + m_abbr(), + m_aranges(), + m_info(), + m_line(), + m_name_to_function_die(), + m_name_to_inlined_die(), + m_name_to_global_die(), + m_name_to_type_die(), + m_indexed(false), +// m_pubnames(), +// m_pubtypes(), +// m_pubbasetypes(), + m_ranges()//, +// m_type_fixups(), +// m_indirect_fixups() +{ +} + +SymbolFileDWARF::~SymbolFileDWARF() +{ +} + +bool +SymbolFileDWARF::SupportedVersion(uint16_t version) +{ + return version == 2 || version == 3; +} + +uint32_t +SymbolFileDWARF::GetAbilities () +{ + uint32_t abilities = 0; + if (m_obj_file != NULL) + { + const Section* section = NULL; + const SectionList *section_list = m_obj_file->GetSectionList(); + if (section_list == NULL) + return 0; + + uint64_t debug_abbrev_file_size = 0; + uint64_t debug_aranges_file_size = 0; + uint64_t debug_frame_file_size = 0; + uint64_t debug_info_file_size = 0; + uint64_t debug_line_file_size = 0; + uint64_t debug_loc_file_size = 0; + uint64_t debug_macinfo_file_size = 0; + uint64_t debug_pubnames_file_size = 0; + uint64_t debug_pubtypes_file_size = 0; + uint64_t debug_ranges_file_size = 0; + uint64_t debug_str_file_size = 0; + + static ConstString g_dwarf_section_name ("__DWARF"); + + section = section_list->FindSectionByName(g_dwarf_section_name).get(); + + if (section) + section->MemoryMapSectionDataFromObjectFile(m_obj_file, m_dwarf_data); + + section = section_list->FindSectionByName (GetSectionNameDebugInfo()).get(); + if (section != NULL) + { + debug_info_file_size = section->GetByteSize(); + + section = section_list->FindSectionByName (GetSectionNameDebugAbbrev()).get(); + if (section) + debug_abbrev_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugAbbrevData); + + section = section_list->FindSectionByName (GetSectionNameDebugAranges()).get(); + if (section) + debug_aranges_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugArangesData); + + section = section_list->FindSectionByName (GetSectionNameDebugFrame()).get(); + if (section) + debug_frame_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugFrameData); + + section = section_list->FindSectionByName (GetSectionNameDebugLine()).get(); + if (section) + debug_line_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugLineData); + + section = section_list->FindSectionByName (GetSectionNameDebugLoc()).get(); + if (section) + debug_loc_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugLocData); + + section = section_list->FindSectionByName (GetSectionNameDebugMacInfo()).get(); + if (section) + debug_macinfo_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugMacInfoData); + + section = section_list->FindSectionByName (GetSectionNameDebugPubNames()).get(); + if (section) + debug_pubnames_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugPubNamesData); + + section = section_list->FindSectionByName (GetSectionNameDebugPubTypes()).get(); + if (section) + debug_pubtypes_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugPubTypesData); + + section = section_list->FindSectionByName (GetSectionNameDebugRanges()).get(); + if (section) + debug_ranges_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugRangesData); + + section = section_list->FindSectionByName (GetSectionNameDebugStr()).get(); + if (section) + debug_str_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugStrData); + } + + if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) + abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; + + if (debug_line_file_size > 0) + abilities |= LineTables; + + if (debug_aranges_file_size > 0) + abilities |= AddressAcceleratorTable; + + if (debug_pubnames_file_size > 0) + abilities |= FunctionAcceleratorTable; + + if (debug_pubtypes_file_size > 0) + abilities |= TypeAcceleratorTable; + + if (debug_macinfo_file_size > 0) + abilities |= MacroInformation; + + if (debug_frame_file_size > 0) + abilities |= CallFrameInformation; + } + return abilities; +} + +const DataExtractor& +SymbolFileDWARF::GetCachedSectionData (uint32_t got_flag, const ConstString §ion_name, DataExtractor &data) +{ + if (m_flags.IsClear (got_flag)) + { + m_flags.Set (got_flag); + const SectionList *section_list = m_obj_file->GetSectionList(); + if (section_list) + { + Section *section = section_list->FindSectionByName (section_name).get(); + if (section ) + { + // See if we memory mapped the DWARF segment? + if (m_dwarf_data.GetByteSize()) + { + data.SetData(m_dwarf_data, section->GetOffset (), section->GetByteSize()); + } + else + { + if (section->ReadSectionDataFromObjectFile(m_obj_file, data) == 0) + data.Clear(); + } + } + } + } + return data; +} + +const DataExtractor& +SymbolFileDWARF::get_debug_abbrev_data() +{ + return GetCachedSectionData (flagsGotDebugAbbrevData, GetSectionNameDebugAbbrev(), m_data_debug_abbrev); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_aranges_data() +{ + return GetCachedSectionData (flagsGotDebugArangesData, GetSectionNameDebugAranges(), m_data_debug_aranges); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_frame_data() +{ + return GetCachedSectionData (flagsGotDebugFrameData, GetSectionNameDebugFrame(), m_data_debug_frame); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_info_data() +{ + return GetCachedSectionData (flagsGotDebugInfoData, GetSectionNameDebugInfo(), m_data_debug_info); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_line_data() +{ + return GetCachedSectionData (flagsGotDebugLineData, GetSectionNameDebugLine(), m_data_debug_line); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_loc_data() +{ + return GetCachedSectionData (flagsGotDebugLocData, GetSectionNameDebugLoc(), m_data_debug_loc); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_macinfo_data() +{ + return GetCachedSectionData (flagsGotDebugMacInfoData, GetSectionNameDebugMacInfo(), m_data_debug_macinfo); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_pubnames_data() +{ + return GetCachedSectionData (flagsGotDebugPubNamesData, GetSectionNameDebugPubNames(), m_data_debug_pubnames); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_pubtypes_data() +{ + return GetCachedSectionData (flagsGotDebugPubTypesData, GetSectionNameDebugPubTypes(), m_data_debug_pubtypes); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_ranges_data() +{ + return GetCachedSectionData (flagsGotDebugRangesData, GetSectionNameDebugRanges(), m_data_debug_ranges); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_str_data() +{ + return GetCachedSectionData (flagsGotDebugStrData, GetSectionNameDebugStr(), m_data_debug_str); +} + + +DWARFDebugAbbrev* +SymbolFileDWARF::DebugAbbrev() +{ + if (m_abbr.get() == NULL) + { + const DataExtractor &debug_abbrev_data = get_debug_abbrev_data(); + if (debug_abbrev_data.GetByteSize() > 0) + { + m_abbr.reset(new DWARFDebugAbbrev()); + if (m_abbr.get()) + m_abbr->Parse(debug_abbrev_data); + } + } + return m_abbr.get(); +} + +const DWARFDebugAbbrev* +SymbolFileDWARF::DebugAbbrev() const +{ + return m_abbr.get(); +} + +DWARFDebugAranges* +SymbolFileDWARF::DebugAranges() +{ + if (m_aranges.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + m_aranges.reset(new DWARFDebugAranges()); + if (m_aranges.get()) + { + const DataExtractor &debug_aranges_data = get_debug_aranges_data(); + if (debug_aranges_data.GetByteSize() > 0) + m_aranges->Extract(debug_aranges_data); + else + m_aranges->Generate(this); + } + } + return m_aranges.get(); +} + +const DWARFDebugAranges* +SymbolFileDWARF::DebugAranges() const +{ + return m_aranges.get(); +} + + +DWARFDebugInfo* +SymbolFileDWARF::DebugInfo() +{ + if (m_info.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + if (get_debug_info_data().GetByteSize() > 0) + { + m_info.reset(new DWARFDebugInfo()); + if (m_info.get()) + { + m_info->SetDwarfData(this); + } + } + } + return m_info.get(); +} + +const DWARFDebugInfo* +SymbolFileDWARF::DebugInfo() const +{ + return m_info.get(); +} + +//DWARFDebugLine* +//SymbolFileDWARF::DebugLine() +//{ +// if (m_line.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__); +// const DataExtractor &debug_line_data = debug_line(); +// if (debug_line_data.GetByteSize() > 0) +// { +// m_line.reset(new DWARFDebugLine()); +// if (m_line.get()) +// m_line->Parse(debug_line_data); +// } +// } +// return m_line.get(); +//} +// +//const DWARFDebugLine* +//SymbolFileDWARF::DebugLine() const +//{ +// return m_line.get(); +//} + + +DWARFCompileUnit* +SymbolFileDWARF::GetDWARFCompileUnitForUID(lldb::user_id_t cu_uid) +{ + DWARFDebugInfo* info = DebugInfo(); + if (info) + return info->GetCompileUnit(cu_uid).get(); + return NULL; +} + +//DWARFCompileUnit* +//SymbolFileDWARF::GetNextUnparsedDWARFCompileUnit(DWARFCompileUnit* prev_cu) +//{ +// DWARFCompileUnit* cu = NULL; +// DWARFDebugInfo* info = DebugInfo(); +// if (info) +// { +// uint32_t cu_idx = 0; +// if (prev_cu != NULL) +// { +// // Find the index of the previus DWARF compile unit if one was provided +// while ((cu = info->GetCompileUnitAtIndex(cu_idx)) != NULL) +// { +// ++cu_idx; +// if (cu == prev_cu) +// break; +// } +// } +// +// // Now find the next unparsed DWARF compile unit. We do this by checking the +// // user data in the DWARFCompileUnit class that starts as NULL, and eventually +// // holds a pointer to the CompileUnit that was created for it after it has +// // been parsed. +// while ((cu = info->GetCompileUnitAtIndex(cu_idx)) != NULL) +// { +// if (cu->GetUserData() == NULL) +// break; +// } +// } +// return cu; +//} + +DWARFDebugRanges* +SymbolFileDWARF::DebugRanges() +{ + if (m_ranges.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + if (get_debug_ranges_data().GetByteSize() > 0) + { + m_ranges.reset(new DWARFDebugRanges()); + if (m_ranges.get()) + m_ranges->Extract(this); + } + } + return m_ranges.get(); +} + +const DWARFDebugRanges* +SymbolFileDWARF::DebugRanges() const +{ + return m_ranges.get(); +} +// +//DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubnames() +//{ +// if (m_pubnames.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); +// const DataExtractor &debug_pubnames_data = get_debug_pubnames_data(); +// if (debug_pubnames_data.GetByteSize() > 0) +// { +// // Pass false to indicate this is a pubnames section +// m_pubnames.reset(new DWARFDebugPubnames()); +// if (m_pubnames.get()) +// { +// // "m_pubnames->GeneratePubnames" is costly, but needed for global variables +// m_pubnames->GeneratePubnames(this); +// +//#if 0 +// StreamFile s(stdout); +// s.Printf (".debug_pubnames for %s/%s:\n", +// m_obj_file->GetModule()->GetFileSpec().GetDirectory().AsCString(), +// m_obj_file->GetModule()->GetFileSpec().GetFilename().AsCString()); +// m_pubnames->Dump (&s); +//#endif +// // "m_pubnames->Extract" is quicker, but the pubnames don't always contain what we need. +// //m_pubnames->Extract(debug_pubnames_data); +// } +// } +// } +// return m_pubnames.get(); +//} +// +//const DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubnames() const +//{ +// return m_pubnames.get(); +//} + +//DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubBaseTypes() +//{ +// if (m_pubbasetypes.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); +// // Pass false to indicate this is a pubnames section +// m_pubbasetypes.reset(new DWARFDebugPubnames()); +// if (m_pubbasetypes.get()) +// m_pubbasetypes->GeneratePubBaseTypes(this); +// } +// return m_pubbasetypes.get(); +//} +// +//const DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubBaseTypes() const +//{ +// return m_pubbasetypes.get(); +//} +// +//const DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubtypes() const +//{ +// return m_pubtypes.get(); +//} +// +//DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubtypes() +//{ +// if (m_pubtypes.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); +// const DataExtractor &debug_pubtypes_data = get_debug_pubtypes_data(); +// if (debug_pubtypes_data.GetByteSize() > 0) +// { +// // Pass false to indicate this is a pubnames section +// m_pubtypes.reset(new DWARFDebugPubnames()); +// if (m_pubtypes.get()) +// m_pubtypes->Extract(debug_pubtypes_data); +// } +// } +// return m_pubtypes.get(); +//} +// + +bool +SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit* cu, CompUnitSP& compile_unit_sp) +{ + if (cu != NULL) + { + const DWARFDebugInfoEntry * cu_die = cu->GetCompileUnitDIEOnly (); + if (cu_die) + { + const char * cu_die_name = cu_die->GetName(this, cu); + const char * cu_comp_dir = cu_die->GetAttributeValueAsString(this, cu, DW_AT_comp_dir, NULL); + Language::Type language = (Language::Type)cu_die->GetAttributeValueAsUnsigned(this, cu, DW_AT_language, 0); + if (cu_die_name) + { + if (cu_die_name[0] == '/' || cu_comp_dir == NULL && cu_comp_dir[0]) + { + compile_unit_sp.reset(new CompileUnit(m_obj_file->GetModule(), cu, cu_die_name, cu->GetOffset(), language)); + } + else + { + std::string fullpath(cu_comp_dir); + if (*fullpath.rbegin() != '/') + fullpath += '/'; + fullpath += cu_die_name; + + compile_unit_sp.reset(new CompileUnit(m_obj_file->GetModule(), cu, fullpath.c_str(), cu->GetOffset(), language)); + } + + if (compile_unit_sp.get()) + { + cu->SetUserData(compile_unit_sp.get()); + return true; + } + } + } + } + return false; +} + +#if defined LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST + +void +SymbolFileDWARF::ShrinkDSYM(CompileUnit *dc_cu, DWARFCompileUnit *dw_cu, const FileSpec& cu_fspec, const FileSpec& base_types_fspec, FSToDIES& fs_to_dies, const DWARFDebugInfoEntry *die) +{ + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_base_type: + // Put all base types into the base type compile unit + fs_to_dies[base_types_fspec].Insert(die); + break; + + default: + { + uint32_t decl_file = die->GetAttributeValueAsUnsigned(this, dw_cu, DW_AT_decl_file, 0); + if (decl_file) + { + fs_to_dies[dc_cu->GetSupportFiles().GetFileSpecAtIndex(decl_file)].Insert(die); + } + else + { + // add this to the current compile unit + fs_to_dies[cu_fspec].Insert(die); + } + } + break; + } + + die = die->GetSibling(); + } +} + + + +#endif + +uint32_t +SymbolFileDWARF::GetNumCompileUnits() +{ + DWARFDebugInfo* info = DebugInfo(); + if (info) + { +#if defined LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST + uint32_t cu_idx; + FSToDIES fs_to_dies; + + FileSpec base_type_fspec("DW_TAG_base_type"); + const uint32_t num_comp_units = info->GetNumCompileUnits(); + + for (cu_idx=0; cu_idx < num_comp_units; ++cu_idx) + { + DWARFCompileUnit* cu = info->GetCompileUnitAtIndex(cu_idx); + if (cu != NULL) + { + const DWARFDebugInfoEntry *cu_die = cu->DIE(); + if (cu_die) + { + CompUnitSP dc_cu_sp; + ParseCompileUnit(cu, dc_cu_sp); + if (dc_cu_sp.get()) + { + FileSpec cu_fspec(*dc_cu_sp.get()); + + ShrinkDSYM(dc_cu_sp.get(), cu, cu_fspec, base_type_fspec, fs_to_dies, cu->DIE()->GetFirstChild()); + } + } + } + } + + Stream strm(stdout); + FSToDIES::const_iterator pos, end = fs_to_dies.end(); + for (pos = fs_to_dies.begin(); pos != end; ++pos) + { + strm << "\n\nMinimal Compile Unit: " << pos->first << ":\n"; + const DWARFDIECollection& dies = pos->second; + dies.Dump(strm, NULL); + } + return num_comp_units; +#else + return info->GetNumCompileUnits(); +#endif + } + return 0; +} + +CompUnitSP +SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) +{ + CompUnitSP comp_unit; + DWARFDebugInfo* info = DebugInfo(); + if (info) + { + DWARFCompileUnit* cu = info->GetCompileUnitAtIndex(cu_idx); + if (cu != NULL) + { + // Our symbol vendor shouldn't be asking us to add a compile unit that + // has already been added to it, which this DWARF plug-in knows as it + // stores the lldb compile unit (CompileUnit) pointer in each + // DWARFCompileUnit object when it gets added. + assert(cu->GetUserData() == NULL); + ParseCompileUnit(cu, comp_unit); + } + } + return comp_unit; +} + +static void +AddRangesToBlock +( + BlockList& blocks, + lldb::user_id_t blockID, + DWARFDebugRanges::RangeList& ranges, + addr_t block_base_addr +) +{ + ranges.SubtractOffset (block_base_addr); + size_t range_idx = 0; + const DWARFDebugRanges::Range *debug_range; + for (range_idx = 0; (debug_range = ranges.RangeAtIndex(range_idx)) != NULL; range_idx++) + { + blocks.AddRange(blockID, debug_range->begin_offset, debug_range->end_offset); + } +} + + +Function * +SymbolFileDWARF::ParseCompileUnitFunction (const SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die) +{ + DWARFDebugRanges::RangeList func_ranges; + const char *name = NULL; + const char *mangled = NULL; + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + DWARFExpression frame_base; + + // Parse the function prototype as a type that can then be added to concrete function instance + ParseTypes (sc, dwarf_cu, die, false, false); + //FixupTypes(); + + if (die->GetDIENamesAndRanges(this, dwarf_cu, name, mangled, func_ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, &frame_base)) + { + // Union of all ranges in the function DIE (if the function is discontiguous) + AddressRange func_range; + lldb::addr_t lowest_func_addr = func_ranges.LowestAddress(0); + lldb::addr_t highest_func_addr = func_ranges.HighestAddress(0); + if (lowest_func_addr != LLDB_INVALID_ADDRESS && lowest_func_addr <= highest_func_addr) + { + func_range.GetBaseAddress().ResolveAddressUsingFileSections (lowest_func_addr, m_obj_file->GetSectionList()); + if (func_range.GetBaseAddress().IsValid()) + func_range.SetByteSize(highest_func_addr - lowest_func_addr); + } + + if (func_range.GetBaseAddress().IsValid()) + { + Mangled func_name; + if (mangled) + func_name.SetValue(mangled, true); + else if (name) + func_name.SetValue(name, false); + + FunctionSP func_sp; + std::auto_ptr decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); + + Type *func_type = NULL; + + if (die->GetUserData() != DIE_IS_BEING_PARSED) + func_type = (Type*)die->GetUserData(); + + assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED); + + func_range.GetBaseAddress().ResolveLinkedAddress(); + + func_sp.reset(new Function (sc.comp_unit, + die->GetOffset(), // UserID is the DIE offset + die->GetOffset(), + func_name, + func_type, + func_range)); // first address range + + if (func_sp.get() != NULL) + { + func_sp->GetFrameBaseExpression() = frame_base; + sc.comp_unit->AddFunction(func_sp); + return func_sp.get(); + } + } + } + return NULL; +} + +size_t +SymbolFileDWARF::ParseCompileUnitFunctions(const SymbolContext &sc) +{ + assert (sc.comp_unit); + size_t functions_added = 0; + const DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + DWARFDIECollection function_dies; + const size_t num_funtions = dwarf_cu->AppendDIEsWithTag (DW_TAG_subprogram, function_dies); + size_t func_idx; + for (func_idx = 0; func_idx < num_funtions; ++func_idx) + { + const DWARFDebugInfoEntry *die = function_dies.GetDIEPtrAtIndex(func_idx); + if (sc.comp_unit->FindFunctionByUID (die->GetOffset()).get() == NULL) + { + if (ParseCompileUnitFunction(sc, dwarf_cu, die)) + ++functions_added; + } + } + //FixupTypes(); + } + return functions_added; +} + +bool +SymbolFileDWARF::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) +{ + assert (sc.comp_unit); + DWARFCompileUnit* cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + assert (cu); + const DWARFDebugInfoEntry * cu_die = cu->GetCompileUnitDIEOnly(); + + if (cu_die) + { + const char * cu_comp_dir = cu_die->GetAttributeValueAsString(this, cu, DW_AT_comp_dir, NULL); + dw_offset_t stmt_list = cu_die->GetAttributeValueAsUnsigned(this, cu, DW_AT_stmt_list, DW_INVALID_OFFSET); + + // All file indexes in DWARF are one based and a file of index zero is + // supposed to be the compile unit itself. + support_files.Append (*sc.comp_unit); + + return DWARFDebugLine::ParseSupportFiles(get_debug_line_data(), cu_comp_dir, stmt_list, support_files); + } + return false; +} + +struct ParseDWARFLineTableCallbackInfo +{ + LineTable* line_table; + const SectionList *section_list; + lldb::addr_t prev_sect_file_base_addr; + lldb::addr_t curr_sect_file_base_addr; + bool is_oso_for_debug_map; + bool prev_in_final_executable; + DWARFDebugLine::Row prev_row; + SectionSP prev_section_sp; + SectionSP curr_section_sp; +}; + +//---------------------------------------------------------------------- +// ParseStatementTableCallback +//---------------------------------------------------------------------- +static void +ParseDWARFLineTableCallback(dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + LineTable* line_table = ((ParseDWARFLineTableCallbackInfo*)userData)->line_table; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // Just started parsing the line table + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table, nothing to do for the cleanup + } + else + { + ParseDWARFLineTableCallbackInfo* info = (ParseDWARFLineTableCallbackInfo*)userData; + // We have a new row, lets append it + + if (info->curr_section_sp.get() == NULL || info->curr_section_sp->ContainsFileAddress(state.address) == false) + { + info->prev_section_sp = info->curr_section_sp; + info->prev_sect_file_base_addr = info->curr_sect_file_base_addr; + // If this is an end sequence entry, then we subtract one from the + // address to make sure we get an address that is not the end of + // a section. + if (state.end_sequence && state.address != 0) + info->curr_section_sp = info->section_list->FindSectionContainingFileAddress (state.address - 1); + else + info->curr_section_sp = info->section_list->FindSectionContainingFileAddress (state.address); + + if (info->curr_section_sp.get()) + info->curr_sect_file_base_addr = info->curr_section_sp->GetFileAddress (); + else + info->curr_sect_file_base_addr = 0; + } + if (info->curr_section_sp.get()) + { + lldb::addr_t curr_line_section_offset = state.address - info->curr_sect_file_base_addr; + // Check for the fancy section magic to determine if we + + if (info->is_oso_for_debug_map) + { + // When this is a debug map object file that contains DWARF + // (referenced from an N_OSO debug map nlist entry) we will have + // a file address in the file range for our section from the + // original .o file, and a load address in the executable that + // contains the debug map. + // + // If the sections for the file range and load range are + // different, we have a remapped section for the function and + // this address is resolved. If they are the same, then the + // function for this address didn't make it into the final + // executable. + bool curr_in_final_executable = info->curr_section_sp->GetLinkedSection () != NULL; + + // If we are doing DWARF with debug map, then we need to carefully + // add each line table entry as there may be gaps as functions + // get moved around or removed. + if (!info->prev_row.end_sequence && info->prev_section_sp.get()) + { + if (info->prev_in_final_executable) + { + bool terminate_previous_entry = false; + if (!curr_in_final_executable) + { + // Check for the case where the previous line entry + // in a function made it into the final executable, + // yet the current line entry falls in a function + // that didn't. The line table used to be contiguous + // through this address range but now it isn't. We + // need to terminate the previous line entry so + // that we can reconstruct the line range correctly + // for it and to keep the line table correct. + terminate_previous_entry = true; + } + else if (info->curr_section_sp.get() != info->prev_section_sp.get()) + { + // Check for cases where the line entries used to be + // contiguous address ranges, but now they aren't. + // This can happen when order files specify the + // ordering of the functions. + lldb::addr_t prev_line_section_offset = info->prev_row.address - info->prev_sect_file_base_addr; + Section *curr_sect = info->curr_section_sp.get(); + Section *prev_sect = info->prev_section_sp.get(); + assert (curr_sect->GetLinkedSection()); + assert (prev_sect->GetLinkedSection()); + lldb::addr_t object_file_addr_delta = state.address - info->prev_row.address; + lldb::addr_t curr_linked_file_addr = curr_sect->GetLinkedFileAddress() + curr_line_section_offset; + lldb::addr_t prev_linked_file_addr = prev_sect->GetLinkedFileAddress() + prev_line_section_offset; + lldb::addr_t linked_file_addr_delta = curr_linked_file_addr - prev_linked_file_addr; + if (object_file_addr_delta != linked_file_addr_delta) + terminate_previous_entry = true; + } + + if (terminate_previous_entry) + { + line_table->InsertLineEntry (info->prev_section_sp, + state.address - info->prev_sect_file_base_addr, + info->prev_row.line, + info->prev_row.column, + info->prev_row.file, + false, // is_stmt + false, // basic_block + false, // state.prologue_end + false, // state.epilogue_begin + true); // end_sequence); + } + } + } + + if (curr_in_final_executable) + { + line_table->InsertLineEntry (info->curr_section_sp, + curr_line_section_offset, + state.line, + state.column, + state.file, + state.is_stmt, + state.basic_block, + state.prologue_end, + state.epilogue_begin, + state.end_sequence); + info->prev_section_sp = info->curr_section_sp; + } + else + { + // If the current address didn't make it into the final + // executable, the current section will be the __text + // segment in the .o file, so we need to clear this so + // we can catch the next function that did make it into + // the final executable. + info->prev_section_sp.reset(); + info->curr_section_sp.reset(); + } + + info->prev_in_final_executable = curr_in_final_executable; + } + else + { + // We are not in an object file that contains DWARF for an + // N_OSO, this is just a normal DWARF file. The DWARF spec + // guarantees that the addresses will be in increasing order + // so, since we store line tables in file address order, we + // can always just append the line entry without needing to + // search for the correct insertion point (we don't need to + // use LineEntry::InsertLineEntry()). + line_table->AppendLineEntry (info->curr_section_sp, + curr_line_section_offset, + state.line, + state.column, + state.file, + state.is_stmt, + state.basic_block, + state.prologue_end, + state.epilogue_begin, + state.end_sequence); + } + } + + info->prev_row = state; + } +} + +bool +SymbolFileDWARF::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + assert (sc.comp_unit); + if (sc.comp_unit->GetLineTable() != NULL) + return true; + + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + const DWARFDebugInfoEntry *dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); + const dw_offset_t cu_line_offset = dwarf_cu_die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_stmt_list, DW_INVALID_OFFSET); + if (cu_line_offset != DW_INVALID_OFFSET) + { + std::auto_ptr line_table_ap(new LineTable(sc.comp_unit)); + if (line_table_ap.get()) + { + ParseDWARFLineTableCallbackInfo info = { line_table_ap.get(), m_obj_file->GetSectionList(), 0, 0, m_flags.IsSet (flagsDWARFIsOSOForDebugMap), false}; + uint32_t offset = cu_line_offset; + DWARFDebugLine::ParseStatementTable(get_debug_line_data(), &offset, ParseDWARFLineTableCallback, &info); + sc.comp_unit->SetLineTable(line_table_ap.release()); + return true; + } + } + } + return false; +} + +size_t +SymbolFileDWARF::ParseFunctionBlocks +( + const SymbolContext& sc, + lldb::user_id_t parentBlockID, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + addr_t subprogram_low_pc, + bool parse_siblings, + bool parse_children +) +{ + size_t blocks_added = 0; + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + { + DWARFDebugRanges::RangeList ranges; + const char *name = NULL; + const char *mangled_name = NULL; + BlockList& blocks = sc.function->GetBlocks(false); + + lldb::user_id_t blockID = blocks.AddChild(parentBlockID, die->GetOffset()); + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + if (die->GetDIENamesAndRanges(this, dwarf_cu, name, mangled_name, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column)) + { + if (tag == DW_TAG_subprogram) + { + assert (subprogram_low_pc == LLDB_INVALID_ADDRESS); + subprogram_low_pc = ranges.LowestAddress(0); + } + + AddRangesToBlock (blocks, blockID, ranges, subprogram_low_pc); + + if (tag != DW_TAG_subprogram && (name != NULL || mangled_name != NULL)) + { + std::auto_ptr decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); + + std::auto_ptr call_ap; + if (call_file != 0 || call_line != 0 || call_column != 0) + call_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(call_file), call_line, call_column)); + + blocks.SetInlinedFunctionInfo(blockID, name, mangled_name, decl_ap.get(), call_ap.get()); + } + + ++blocks_added; + + if (parse_children && die->HasChildren()) + { + blocks_added += ParseFunctionBlocks(sc, blockID, dwarf_cu, die->GetFirstChild(), subprogram_low_pc, true, true); + } + } + } + break; + default: + break; + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + return blocks_added; +} + +size_t +SymbolFileDWARF::ParseChildMembers +( + const SymbolContext& sc, + TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + std::vector& base_classes, + std::vector& member_accessibilities, + int& default_accessibility, + bool &is_a_class +) +{ + if (parent_die == NULL) + return 0; + + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + + size_t count = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_member: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + Declaration decl; + DWARFExpression location; + const char *name = NULL; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + uint32_t accessibility = clang::AS_none; + off_t member_offset = 0; + size_t byte_size = 0; + size_t bit_offset = 0; + size_t bit_size = 0; + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_bit_offset: bit_offset = form_value.Unsigned(); break; + case DW_AT_bit_size: bit_size = form_value.Unsigned(); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_data_member_location: + if (form_value.BlockData()) + { + Value initialValue(0); + Value memberOffset(0); + const DataExtractor& debug_info_data = get_debug_info_data(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate(NULL, NULL, debug_info_data, NULL, NULL, block_offset, block_length, eRegisterKindDWARF, &initialValue, memberOffset, NULL)) + { + member_offset = memberOffset.ResolveValue(NULL, NULL).UInt(); + } + } + break; + + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility (form_value.Unsigned()); break; + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_mutable: + case DW_AT_visibility: + default: + case DW_AT_sibling: + break; + } + } + } + + Type *member_type = ResolveTypeUID(encoding_uid); + assert(member_type); + if (accessibility == clang::AS_none) + accessibility = default_accessibility; + member_accessibilities.push_back(accessibility); + + type_list->GetClangASTContext().AddFieldToRecordType (type_sp->GetOpaqueClangQualType(), name, member_type->GetOpaqueClangQualType(), accessibility, bit_size); + } + } + break; + + case DW_TAG_subprogram: + { + is_a_class = true; + if (default_accessibility == clang::AS_none) + default_accessibility = clang::AS_private; + // TODO: implement DW_TAG_subprogram type parsing +// UserDefTypeChildInfo method_info(die->GetOffset()); +// +// FunctionSP func_sp (sc.comp_unit->FindFunctionByUID (die->GetOffset())); +// if (func_sp.get() == NULL) +// ParseCompileUnitFunction(sc, dwarf_cu, die); +// +// method_info.SetEncodingTypeUID(die->GetOffset()); +// struct_udt->AddMethod(method_info); + } + break; + + case DW_TAG_inheritance: + { + is_a_class = true; + if (default_accessibility == clang::AS_none) + default_accessibility = clang::AS_private; + // TODO: implement DW_TAG_inheritance type parsing + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + Declaration decl; + DWARFExpression location; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + uint32_t accessibility = default_accessibility; + bool is_virtual = false; + bool is_base_of_class = true; + off_t member_offset = 0; + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_data_member_location: + if (form_value.BlockData()) + { + Value initialValue(0); + Value memberOffset(0); + const DataExtractor& debug_info_data = get_debug_info_data(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate(NULL, NULL, debug_info_data, NULL, NULL, block_offset, block_length, eRegisterKindDWARF, &initialValue, memberOffset, NULL)) + { + member_offset = memberOffset.ResolveValue(NULL, NULL).UInt(); + } + } + break; + + case DW_AT_accessibility: + accessibility = DwarfToClangAccessibility(form_value.Unsigned()); + break; + + case DW_AT_virtuality: is_virtual = form_value.Unsigned() != 0; break; + default: + case DW_AT_sibling: + break; + } + } + } + + Type *base_class_dctype = ResolveTypeUID(encoding_uid); + assert(base_class_dctype); + base_classes.push_back (type_list->GetClangASTContext().CreateBaseClassSpecifier (base_class_dctype->GetOpaqueClangQualType(), accessibility, is_virtual, is_base_of_class)); + assert(base_classes.back()); + } + } + break; + + default: + break; + } + } + return count; +} + + +clang::DeclContext* +SymbolFileDWARF::GetClangDeclContextForTypeUID (lldb::user_id_t type_uid) +{ + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = debug_info->GetDIEPtr(type_uid, &cu_sp); + if (die) + return GetClangDeclContextForDIE (cu_sp.get(), die); + } + return NULL; +} + +Type* +SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) +{ + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + const DWARFDebugInfoEntry* type_die = debug_info->GetDIEPtr(type_uid, NULL); + if (type_die != NULL) + { + void *type = type_die->GetUserData(); + if (type == NULL) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = debug_info->GetDIEPtr(type_uid, &cu_sp); + if (die != NULL) + { + TypeSP owning_type_sp; + TypeSP type_sp(GetTypeForDIE(cu_sp.get(), die, owning_type_sp, 0, 0)); + } + type = type_die->GetUserData(); + } + if (type != DIE_IS_BEING_PARSED) + return (Type *)type; + } + } + return NULL; +} + +CompileUnit* +SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit* cu, uint32_t cu_idx) +{ + // Check if the symbol vendor already knows about this compile unit? + if (cu->GetUserData() == NULL) + { + // The symbol vendor doesn't know about this compile unit, we + // need to parse and add it to the symbol vendor object. + CompUnitSP dc_cu; + ParseCompileUnit(cu, dc_cu); + if (dc_cu.get()) + { + // Figure out the compile unit index if we weren't given one + if (cu_idx == UINT_MAX) + DebugInfo()->GetCompileUnit(cu->GetOffset(), &cu_idx); + + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(dc_cu, cu_idx); + } + } + return (CompileUnit*)cu->GetUserData(); +} + +bool +SymbolFileDWARF::GetFunction (DWARFCompileUnit* cu, const DWARFDebugInfoEntry* func_die, SymbolContext& sc) +{ + sc.Clear(); + // Check if the symbol vendor already knows about this compile unit? + sc.module_sp = m_obj_file->GetModule()->GetSP(); + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, UINT_MAX); + + sc.function = sc.comp_unit->FindFunctionByUID (func_die->GetOffset()).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, cu, func_die); + + return sc.function != NULL; +} + +uint32_t +SymbolFileDWARF::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "SymbolFileDWARF::ResolveSymbolContext (so_addr = { section = %p, offset = 0x%llx }, resolve_scope = 0x%8.8x)", + so_addr.GetSection(), + so_addr.GetOffset(), + resolve_scope); + uint32_t resolved = 0; + if (resolve_scope & ( eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextBlock | + eSymbolContextLineEntry)) + { + lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); + + DWARFDebugAranges* debug_aranges = DebugAranges(); + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_aranges) + { + dw_offset_t cu_offset = debug_aranges->FindAddress(file_vm_addr); + if (cu_offset != DW_INVALID_OFFSET) + { + uint32_t cu_idx; + DWARFCompileUnit* cu = debug_info->GetCompileUnit(cu_offset, &cu_idx).get(); + if (cu) + { + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, cu_idx); + assert(sc.comp_unit != NULL); + resolved |= eSymbolContextCompUnit; + + if (resolve_scope & eSymbolContextLineEntry) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table == NULL) + { + if (ParseCompileUnitLineTable(sc)) + line_table = sc.comp_unit->GetLineTable(); + } + if (line_table != NULL) + { + if (so_addr.IsLinkedAddress()) + { + Address linked_addr (so_addr); + linked_addr.ResolveLinkedAddress(); + if (line_table->FindLineEntryByAddress (linked_addr, sc.line_entry)) + { + resolved |= eSymbolContextLineEntry; + } + } + else if (line_table->FindLineEntryByAddress (so_addr, sc.line_entry)) + { + resolved |= eSymbolContextLineEntry; + } + } + } + + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) + { + DWARFDebugInfoEntry *function_die = NULL; + DWARFDebugInfoEntry *block_die = NULL; + if (resolve_scope & eSymbolContextBlock) + { + cu->LookupAddress(file_vm_addr, &function_die, &block_die); + } + else + { + cu->LookupAddress(file_vm_addr, &function_die, NULL); + } + + if (function_die != NULL) + { + sc.function = sc.comp_unit->FindFunctionByUID (function_die->GetOffset()).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, cu, function_die); + } + + if (sc.function != NULL) + { + resolved |= eSymbolContextFunction; + + if (resolve_scope & eSymbolContextBlock) + { + BlockList& blocks = sc.function->GetBlocks(true); + + if (block_die != NULL) + sc.block = blocks.GetBlockByID(block_die->GetOffset()); + else + sc.block = blocks.GetBlockByID(function_die->GetOffset()); + if (sc.block) + resolved |= eSymbolContextBlock; + } + } + } + } + } + } + } + return resolved; +} + + + +uint32_t +SymbolFileDWARF::ResolveSymbolContext(const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + const uint32_t prev_size = sc_list.GetSize(); + if (resolve_scope & eSymbolContextCompUnit) + { + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + uint32_t cu_idx; + DWARFCompileUnit* cu = NULL; + + for (cu_idx = 0; (cu = debug_info->GetCompileUnitAtIndex(cu_idx)) != NULL; ++cu_idx) + { + CompileUnit *dc_cu = GetCompUnitForDWARFCompUnit(cu, cu_idx); + bool file_spec_matches_cu_file_spec = dc_cu != NULL && FileSpec::Compare(file_spec, *dc_cu, false) == 0; + if (check_inlines || file_spec_matches_cu_file_spec) + { + SymbolContext sc (m_obj_file->GetModule()); + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, cu_idx); + assert(sc.comp_unit != NULL); + + uint32_t file_idx = UINT32_MAX; + + // If we are looking for inline functions only and we don't + // find it in the support files, we are done. + if (check_inlines) + { + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec); + if (file_idx == UINT32_MAX) + continue; + } + + if (line != 0) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table != NULL && line != 0) + { + // We will have already looked up the file index if + // we are searching for inline entries. + if (!check_inlines) + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec); + + if (file_idx != UINT32_MAX) + { + uint32_t found_line; + uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_idx, line, false, &sc.line_entry); + found_line = sc.line_entry.line; + + while (line_idx != UINT_MAX) + { + sc.function = NULL; + sc.block = NULL; + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) + { + const lldb::addr_t file_vm_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); + if (file_vm_addr != LLDB_INVALID_ADDRESS) + { + DWARFDebugInfoEntry *function_die = NULL; + DWARFDebugInfoEntry *block_die = NULL; + cu->LookupAddress(file_vm_addr, &function_die, resolve_scope & eSymbolContextBlock ? &block_die : NULL); + + if (function_die != NULL) + { + sc.function = sc.comp_unit->FindFunctionByUID (function_die->GetOffset()).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, cu, function_die); + } + + if (sc.function != NULL) + { + BlockList& blocks = sc.function->GetBlocks(true); + + if (block_die != NULL) + sc.block = blocks.GetBlockByID(block_die->GetOffset()); + else + sc.block = blocks.GetBlockByID(function_die->GetOffset()); + } + } + } + + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_idx, found_line, true, &sc.line_entry); + } + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + + if (!check_inlines) + break; + } + } + } + } + return sc_list.GetSize() - prev_size; +} + +void +SymbolFileDWARF::Index () +{ + if (m_indexed) + return; + m_indexed = true; + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::Index (%s)", + GetObjectFile()->GetFileSpec().GetFilename().AsCString()); + + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + + bool clear_dies = cu->ExtractDIEsIfNeeded (false) > 1; + + cu->Index (m_name_to_function_die, + m_name_to_inlined_die, + m_name_to_global_die, + m_name_to_type_die); + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + cu->ClearDIEs (true); + } + + m_name_to_function_die.Sort(); + m_name_to_inlined_die.Sort(); + m_name_to_global_die.Sort(); + m_name_to_type_die.Sort(); + } +} + +uint32_t +SymbolFileDWARF::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + std::vector die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + const UniqueCStringMap::Entry *entry; + + for (entry = m_name_to_global_die.FindFirstValueForName (name.AsCString()); + entry != NULL; + entry = m_name_to_global_die.FindNextValueForName (name.AsCString(), entry)) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr (entry->value, &cu_sp); + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule()->GetSP(); + assert (sc.module_sp); + + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, UINT_MAX); + assert(sc.comp_unit != NULL); + + ParseVariables(sc, cu_sp.get(), die, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + break; + } + } + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARF::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + std::vector die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + // Create the pubnames information so we can quickly lookup external symbols by name + const size_t num_entries = m_name_to_global_die.GetSize(); + for (size_t i=0; iGetDIEPtr (die_offset, &cu_sp); + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule()->GetSP(); + assert (sc.module_sp); + + + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, UINT_MAX); + assert(sc.comp_unit != NULL); + + ParseVariables(sc, cu_sp.get(), die, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + break; + } + } + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARF::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::FindFunctions (name = '%s')", + name.AsCString()); + + std::vector die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = sc_list.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + const UniqueCStringMap::Entry *entry; + + SymbolContext sc; + for (entry = m_name_to_function_die.FindFirstValueForName (name.AsCString()); + entry != NULL; + entry = m_name_to_function_die.FindNextValueForName (name.AsCString(), entry)) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr (entry->value, &cu_sp); + if (die) + { + if (GetFunction (cu_sp.get(), die, sc)) + { + // We found the function, so we should find the line table + // and line table entry as well + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table == NULL) + { + if (ParseCompileUnitLineTable(sc)) + line_table = sc.comp_unit->GetLineTable(); + } + if (line_table != NULL) + line_table->FindLineEntryByAddress (sc.function->GetAddressRange().GetBaseAddress(), sc.line_entry); + + sc_list.Append(sc); + } + } + } + + // Return the number of variable that were appended to the list + return sc_list.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARF::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::FindFunctions (regex = '%s')", + regex.GetText()); + + std::vector die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = sc_list.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + // Create the pubnames information so we can quickly lookup external symbols by name + // Create the pubnames information so we can quickly lookup external symbols by name + const size_t num_entries = m_name_to_function_die.GetSize(); + SymbolContext sc; + for (size_t i=0; iGetDIEPtr (die_offset, &cu_sp); + if (die) + { + if (GetFunction (cu_sp.get(), die, sc)) + { + // We found the function, so we should find the line table + // and line table entry as well + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table == NULL) + { + if (ParseCompileUnitLineTable(sc)) + line_table = sc.comp_unit->GetLineTable(); + } + if (line_table != NULL) + line_table->FindLineEntryByAddress (sc.function->GetAddressRange().GetBaseAddress(), sc.line_entry); + + + sc_list.Append(sc); + } + } + } + + // Return the number of variable that were appended to the list + return sc_list.GetSize() - original_size; +} + +#if 0 +uint32_t +SymbolFileDWARF::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +{ + // If we aren't appending the results to this list, then clear the list + if (!append) + types.Clear(); + + // Create the pubnames information so we can quickly lookup external symbols by name + DWARFDebugPubnames* pubtypes = DebugPubtypes(); + if (pubtypes) + { + std::vector die_offsets; + if (!pubtypes->Find(name.AsCString(), false, die_offsets)) + { + DWARFDebugPubnames* pub_base_types = DebugPubBaseTypes(); + if (pub_base_types && !pub_base_types->Find(name.AsCString(), false, die_offsets)) + return 0; + } + return FindTypes(die_offsets, max_matches, encoding, udt_uid, types); + } + return 0; +} + + +uint32_t +SymbolFileDWARF::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +{ + // If we aren't appending the results to this list, then clear the list + if (!append) + types.Clear(); + + // Create the pubnames information so we can quickly lookup external symbols by name + DWARFDebugPubnames* pubtypes = DebugPubtypes(); + if (pubtypes) + { + std::vector die_offsets; + if (!pubtypes->Find(regex, die_offsets)) + { + DWARFDebugPubnames* pub_base_types = DebugPubBaseTypes(); + if (pub_base_types && !pub_base_types->Find(regex, die_offsets)) + return 0; + } + + return FindTypes(die_offsets, max_matches, encoding, udt_uid, types); + } + + return 0; +} + + + +uint32_t +SymbolFileDWARF::FindTypes(std::vector die_offsets, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +{ + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = types.Size(); + + const uint32_t num_die_offsets = die_offsets.size(); + // Parse all of the types we found from the pubtypes matches + uint32_t i; + uint32_t num_matches = 0; + for (i = 0; i < num_die_offsets; ++i) + { + dw_offset_t die_offset = die_offsets[i]; + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr(die_offset, &cu_sp); + + assert(die != NULL); + + bool get_type_for_die = true; + if (encoding) + { + // Check if this type has already been uniqued and registers with the module? + Type* type = (Type*)die->GetUserData(); + if (type != NULL && type != DIE_IS_BEING_PARSED) + { + get_type_for_die = type->GetEncoding() == encoding; + } + else + { + dw_tag_t tag = die->Tag(); + switch (encoding) + { + case Type::address: + case Type::boolean: + case Type::complex_float: + case Type::float_type: + case Type::signed_int: + case Type::signed_char: + case Type::unsigned_int: + case Type::unsigned_char: + case Type::imaginary_float: + case Type::packed_decimal: + case Type::numeric_string: + case Type::edited_string: + case Type::signed_fixed: + case Type::unsigned_fixed: + case Type::decimal_float: + if (tag != DW_TAG_base_type) + get_type_for_die = false; + else + { + if (die->GetAttributeValueAsUnsigned(this, cu_sp.get(), DW_AT_encoding, Type::invalid) != encoding) + get_type_for_die = false; + } + break; + + case Type::indirect_const: get_type_for_die = tag == DW_TAG_const_type; break; + case Type::indirect_restrict: get_type_for_die = tag == DW_TAG_restrict_type; break; + case Type::indirect_volatile: get_type_for_die = tag == DW_TAG_volatile_type; break; + case Type::indirect_typedef: get_type_for_die = tag == DW_TAG_typedef; break; + case Type::indirect_pointer: get_type_for_die = tag == DW_TAG_pointer_type; break; + case Type::indirect_reference: get_type_for_die = tag == DW_TAG_reference_type; break; + + case Type::user_defined_type: + switch (tag) + { + case DW_TAG_array_type: + get_type_for_die = UserDefTypeArray::OwnsUserDefTypeUID(udt_uid); + break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + get_type_for_die = UserDefTypeStruct::OwnsUserDefTypeUID(udt_uid); + break; + + case DW_TAG_enumeration_type: + get_type_for_die = UserDefTypeEnum::OwnsUserDefTypeUID(udt_uid); + break; + + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + get_type_for_die = UserDefTypeFunction::OwnsUserDefTypeUID(udt_uid); + break; + } + } + } + } + + if (get_type_for_die) + { + TypeSP owning_type_sp; + TypeSP type_sp(GetTypeForDIE(cu_sp.get(), die, owning_type_sp, NULL, 0, 0)); + + if (type_sp.get()) + { + // See if we are filtering results based on encoding? + bool add_type = (encoding == Type::invalid); + if (!add_type) + { + // We are filtering base on encoding, so lets check the resulting type encoding + add_type = (encoding == type_sp->GetEncoding()); + if (add_type) + { + // The type encoding matches, if this is a user defined type, lets + // make sure the exact user define type uid matches if one was provided + if (encoding == Type::user_defined_type && udt_uid != LLDB_INVALID_UID) + { + UserDefType* udt = type_sp->GetUserDefinedType().get(); + if (udt) + add_type = udt->UserDefinedTypeUID() == udt_uid; + } + } + } + // Add the type to our list as long as everything matched + if (add_type) + { + types.InsertUnique(type_sp); + if (++num_matches >= max_matches) + break; + } + } + } + } + + // Return the number of variable that were appended to the list + return types.Size() - original_size; +} + +#endif + + +size_t +SymbolFileDWARF::ParseChildParameters +( + const SymbolContext& sc, + TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + TypeList* type_list, + std::vector& function_param_types, + std::vector& function_param_decls +) +{ + if (parent_die == NULL) + return 0; + + size_t count = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + switch (tag) + { + case DW_TAG_formal_parameter: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + const char *name = NULL; + Declaration decl; + dw_offset_t param_type_die_offset = DW_INVALID_OFFSET; + // one of None, Auto, Register, Extern, Static, PrivateExtern + + clang::VarDecl::StorageClass storage = clang::VarDecl::None; + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: param_type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_location: + // if (form_value.BlockData()) + // { + // const DataExtractor& debug_info_data = debug_info(); + // uint32_t block_length = form_value.Unsigned(); + // DataExtractor location(debug_info_data, form_value.BlockData() - debug_info_data.GetDataStart(), block_length); + // } + // else + // { + // } + // break; + case DW_AT_artificial: + case DW_AT_const_value: + case DW_AT_default_value: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_is_optional: + case DW_AT_segment: + case DW_AT_variable_parameter: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + Type *dc_type = ResolveTypeUID(param_type_die_offset); + if (dc_type) + { + function_param_types.push_back (dc_type->GetOpaqueClangQualType()); + + clang::ParmVarDecl *param_var_decl = type_list->GetClangASTContext().CreateParmeterDeclaration (name, dc_type->GetOpaqueClangQualType(), storage); + assert(param_var_decl); + function_param_decls.push_back(param_var_decl); + } + } + } + break; + + default: + break; + } + } + return count; +} + +size_t +SymbolFileDWARF::ParseChildEnumerators +( + const SymbolContext& sc, + TypeSP& type_sp, + void * enumerator_qual_type, + uint32_t enumerator_byte_size, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die +) +{ + if (parent_die == NULL) + return 0; + + size_t enumerators_added = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + if (tag == DW_TAG_enumerator) + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + int64_t enum_value = 0; + Declaration decl; + + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_sibling: + break; + } + } + } + + if (name && name[0] && got_value) + { + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + type_list->GetClangASTContext().AddEnumerationValueToEnumerationType (type_sp->GetOpaqueClangQualType(), enumerator_qual_type, decl, name, enum_value, enumerator_byte_size * 8); + ++enumerators_added; + } + } + } + } + return enumerators_added; +} + +void +SymbolFileDWARF::ParseChildArrayInfo +( + const SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + int64_t& first_index, + std::vector& element_orders, + uint32_t& byte_stride, + uint32_t& bit_stride +) +{ + if (parent_die == NULL) + return; + + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + switch (tag) + { + case DW_TAG_enumerator: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + int64_t enum_value = 0; + + uint32_t i; + for (i=0; iGetAttributes(this, dwarf_cu, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + uint64_t byte_size = 0; + int64_t enum_value = 0; + uint64_t num_elements = 0; + uint64_t lower_bound = 0; + uint64_t upper_bound = 0; + uint32_t i; + for (i=0; iGetOffset(), DW_TAG_value_to_name(tag), attributes.die_offsets[i], DW_AT_value_to_name(attr)); // remove this, debug only + + case DW_AT_abstract_origin: + case DW_AT_accessibility: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_sibling: + case DW_AT_threads_scaled: + case DW_AT_type: + case DW_AT_visibility: + break; + } + } + } + + if (upper_bound > lower_bound) + num_elements = upper_bound - lower_bound + 1; + + if (num_elements > 0) + element_orders.push_back (num_elements); + } + } + break; + } + } +} + +Type* +SymbolFileDWARF::GetUniquedTypeForDIEOffset(dw_offset_t type_die_offset, TypeSP& owning_type_sp, int32_t child_type, uint32_t idx, bool safe) +{ + if (type_die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* type_die = DebugInfo()->GetDIEPtr(type_die_offset, &cu_sp); + assert(type_die != NULL); + GetTypeForDIE(cu_sp.get(), type_die, owning_type_sp, child_type, idx); + // Return the uniqued type if there is one + Type* type = (Type*)type_die->GetUserData(); + if (type == DIE_IS_BEING_PARSED && safe) + return NULL; + return type; + } + return NULL; +} + +TypeSP +SymbolFileDWARF::GetTypeForDIE(DWARFCompileUnit *cu, const DWARFDebugInfoEntry* die, TypeSP& owning_type_sp, int32_t child_type, uint32_t idx) +{ + TypeSP type_sp; + if (die != NULL) + { + assert(cu != NULL); + Type *type_ptr = (Type *)die->GetUserData(); + if (type_ptr == NULL) + { + SymbolContext sc(GetCompUnitForDWARFCompUnit(cu)); + bool type_is_new = false; + type_sp = ParseType(sc, cu, die, type_is_new); + type_ptr = (Type *)die->GetUserData(); + if (owning_type_sp.get() == NULL) + owning_type_sp = type_sp; + } + else if (type_ptr != DIE_IS_BEING_PARSED) + { + // Grab the existing type from the master types lists + type_sp = m_obj_file->GetModule()->GetTypeList()->FindType(type_ptr->GetID()); + } + + } + return type_sp; +} + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextForDIEOffset (dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr(die_offset, &cu_sp); + return GetClangDeclContextForDIE (cu_sp.get(), die); + } + return NULL; +} + + + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextForDIE (const DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die) +{ + DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die); + if (pos != m_die_to_decl_ctx.end()) + return pos->second; + + while (die != NULL) + { + switch (die->Tag()) + { + case DW_TAG_namespace: + { + const char *namespace_name = die->GetAttributeValueAsString(this, cu, DW_AT_name, NULL); + if (namespace_name) + { + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + assert(type_list); + Declaration decl; // TODO: fill in the decl object + clang::NamespaceDecl *namespace_decl = type_list->GetClangASTContext().GetUniqueNamespaceDeclaration (namespace_name, decl, GetClangDeclContextForDIE (cu, die->GetParent())); + if (namespace_decl) + m_die_to_decl_ctx[die] = (clang::DeclContext*)namespace_decl; + return namespace_decl; + } + } + break; + + default: + break; + } + clang::DeclContext *decl_ctx; + decl_ctx = GetClangDeclContextForDIEOffset (die->GetAttributeValueAsUnsigned(this, cu, DW_AT_specification, DW_INVALID_OFFSET)); + if (decl_ctx) + return decl_ctx; + + decl_ctx = GetClangDeclContextForDIEOffset (die->GetAttributeValueAsUnsigned(this, cu, DW_AT_abstract_origin, DW_INVALID_OFFSET)); + if (decl_ctx) + return decl_ctx; + + die = die->GetParent(); + } + return NULL; +} + +TypeSP +SymbolFileDWARF::ParseType(const SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool &type_is_new) +{ + TypeSP type_sp; + + uint32_t accessibility = clang::AS_none; + if (die != NULL) + { + dw_tag_t tag = die->Tag(); + if (die->GetUserData() == NULL) + { + type_is_new = true; + + bool is_forward_declaration = false; + DWARFDebugInfoEntry::Attributes attributes; + const char *type_name_cstr = NULL; + ConstString type_name_dbstr; + Type::EncodingUIDType encoding_uid_type = Type::eIsTypeWithUID; + void *clang_type = NULL; + + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + dw_attr_t attr; + + switch (tag) + { + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast(die)->SetUserData(DIE_IS_BEING_PARSED); + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + Declaration decl; + uint32_t encoding = 0; + size_t byte_size = 0; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + + if (num_attributes > 0) + { + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_encoding: encoding = form_value.Unsigned(); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + default: + case DW_AT_sibling: + break; + } + } + } + } + + switch (tag) + { + default: + case DW_TAG_base_type: + clang_type = type_list->GetClangASTContext().GetBuiltinTypeForDWARFEncodingAndBitSize (type_name_cstr, encoding, byte_size * 8); + break; + + case DW_TAG_pointer_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::ePointerToTypeWithUID; + break; + + case DW_TAG_reference_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eLValueReferenceToTypeWithUID; + break; + + case DW_TAG_typedef: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eTypedefToTypeWithUID; + break; + + case DW_TAG_const_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eIsConstTypeWithUID; //ClangASTContext::AddConstModifier (clang_type); + break; + + case DW_TAG_restrict_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eIsRestrictTypeWithUID; //ClangASTContext::AddRestrictModifier (clang_type); + break; + + case DW_TAG_volatile_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eIsVolatileTypeWithUID; //ClangASTContext::AddVolatileModifier (clang_type); + break; + } + + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, byte_size, NULL, encoding_uid, encoding_uid_type, &decl, clang_type)); + + const_cast(die)->SetUserData(type_sp.get()); + + +// Type* encoding_type = GetUniquedTypeForDIEOffset(encoding_uid, type_sp, NULL, 0, 0, false); +// if (encoding_type != NULL) +// { +// if (encoding_type != DIE_IS_BEING_PARSED) +// type_sp->SetEncodingType(encoding_type); +// else +// m_indirect_fixups.push_back(type_sp.get()); +// } + } + break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast(die)->SetUserData(DIE_IS_BEING_PARSED); + + size_t byte_size = 0; + //bool struct_is_class = false; + Declaration decl; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_start_scope: + case DW_AT_visibility: + default: + case DW_AT_sibling: + break; + } + } + } + } + + int tag_decl_kind = -1; + int default_accessibility = clang::AS_none; + if (tag == DW_TAG_structure_type) + { + tag_decl_kind = clang::TTK_Struct; + default_accessibility = clang::AS_public; + } + else if (tag == DW_TAG_union_type) + { + tag_decl_kind = clang::TTK_Union; + default_accessibility = clang::AS_public; + } + else if (tag == DW_TAG_class_type) + { + tag_decl_kind = clang::TTK_Class; + default_accessibility = clang::AS_private; + } + + assert (tag_decl_kind != -1); + clang_type = type_list->GetClangASTContext().CreateRecordType (type_name_cstr, tag_decl_kind, GetClangDeclContextForDIE (dwarf_cu, die)); + + m_die_to_decl_ctx[die] = ClangASTContext::GetDeclContextForType (clang_type); + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, byte_size, NULL, LLDB_INVALID_UID, Type::eIsTypeWithUID, &decl, clang_type)); + + const_cast(die)->SetUserData(type_sp.get()); + +// assert(type_sp.get()); +// if (accessibility) +// type_sp->SetAccess(accessibility); +// + type_list->GetClangASTContext().StartTagDeclarationDefinition (clang_type); + if (die->HasChildren()) + { + std::vector base_classes; + std::vector member_accessibilities; + bool is_a_class = false; + ParseChildMembers(sc, type_sp, dwarf_cu, die, base_classes, member_accessibilities, default_accessibility, is_a_class); + // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we + // need to tell the clang type it is actually a class. + if (is_a_class && tag_decl_kind != clang::TTK_Class) + type_list->GetClangASTContext().SetTagTypeKind (clang_type, clang::TTK_Class); + + // Since DW_TAG_structure_type gets used for both classes + // and structures, we may need to set any DW_TAG_member + // fields to have a "private" access if none was specified. + // When we parsed the child members we tracked that actual + // accessibility value for each DW_TAG_member in the + // "member_accessibilities" array. If the value for the + // member is zero, then it was set to the "default_accessibility" + // which for structs was "public". Below we correct this + // by setting any fields to "private" that weren't correctly + // set. + if (is_a_class && !member_accessibilities.empty()) + { + // This is a class and all members that didn't have + // their access specified are private. + type_list->GetClangASTContext().SetDefaultAccessForRecordFields (clang_type, clang::AS_private, member_accessibilities.data(), member_accessibilities.size()); + } + + if (!base_classes.empty()) + { + type_list->GetClangASTContext().SetBaseClassesForClassType (clang_type, base_classes.data(), base_classes.size()); + } + } + type_list->GetClangASTContext().CompleteTagDeclarationDefinition (clang_type); + } + break; + + case DW_TAG_enumeration_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast(die)->SetUserData(DIE_IS_BEING_PARSED); + + size_t byte_size = 0; + lldb::user_id_t encoding_uid = DW_INVALID_OFFSET; + Declaration decl; + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_bit_stride: + case DW_AT_byte_stride: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_start_scope: + case DW_AT_visibility: + case DW_AT_specification: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + clang_type = type_list->GetClangASTContext().CreateEnumerationType(decl, type_name_cstr); + m_die_to_decl_ctx[die] = ClangASTContext::GetDeclContextForType (clang_type); + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, byte_size, NULL, encoding_uid, Type::eIsTypeWithUID, &decl, clang_type)); + + const_cast(die)->SetUserData(type_sp.get()); + + if (die->HasChildren()) + { + type_list->GetClangASTContext().StartTagDeclarationDefinition (clang_type); + void *enumerator_qual_type = type_list->GetClangASTContext().GetBuiltinTypeForDWARFEncodingAndBitSize (NULL, DW_ATE_signed, byte_size * 8); + ParseChildEnumerators(sc, type_sp, enumerator_qual_type, byte_size, dwarf_cu, die); + type_list->GetClangASTContext().CompleteTagDeclarationDefinition (clang_type); + } + } + } + break; + + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast(die)->SetUserData(DIE_IS_BEING_PARSED); + + const char *mangled = NULL; + dw_offset_t type_die_offset = DW_INVALID_OFFSET; + Declaration decl; + bool isVariadic = false; + bool is_inline = false; + unsigned type_quals = 0; + clang::FunctionDecl::StorageClass storage = clang::FunctionDecl::None;//, Extern, Static, PrivateExtern + + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + + case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_external: + if (form_value.Unsigned()) + { + if (storage == clang::FunctionDecl::None) + storage = clang::FunctionDecl::Extern; + else + storage = clang::FunctionDecl::PrivateExtern; + } + break; + case DW_AT_inline: + is_inline = form_value.Unsigned() != 0; + break; + + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_address_class: + case DW_AT_artificial: + case DW_AT_calling_convention: + case DW_AT_data_location: + case DW_AT_elemental: + case DW_AT_entry_pc: + case DW_AT_explicit: + case DW_AT_frame_base: + case DW_AT_high_pc: + case DW_AT_low_pc: + case DW_AT_object_pointer: + case DW_AT_prototyped: + case DW_AT_pure: + case DW_AT_ranges: + case DW_AT_recursive: + case DW_AT_return_addr: + case DW_AT_segment: + case DW_AT_specification: + case DW_AT_start_scope: + case DW_AT_static_link: + case DW_AT_trampoline: + case DW_AT_visibility: + case DW_AT_virtuality: + case DW_AT_vtable_elem_location: + case DW_AT_abstract_origin: + case DW_AT_description: + case DW_AT_sibling: + break; + } + } + } + + void *return_clang_type = NULL; + Type *func_type = ResolveTypeUID(type_die_offset); + if (func_type) + return_clang_type = func_type->GetOpaqueClangQualType(); + else + return_clang_type = type_list->GetClangASTContext().GetVoidBuiltInType(); + + std::vector function_param_types; + std::vector function_param_decls; + + // Parse the function children for the parameters + ParseChildParameters(sc, type_sp, dwarf_cu, die, type_list, function_param_types, function_param_decls); + + clang_type = type_list->GetClangASTContext().CreateFunctionType (return_clang_type, &function_param_types[0], function_param_types.size(), isVariadic, type_quals); + if (type_name_cstr) + { + clang::FunctionDecl *function_decl = type_list->GetClangASTContext().CreateFunctionDeclaration (type_name_cstr, clang_type, storage, is_inline); + // Add the decl to our DIE to decl context map + assert (function_decl); + m_die_to_decl_ctx[die] = function_decl; + if (!function_param_decls.empty()) + type_list->GetClangASTContext().SetFunctionParameters (function_decl, function_param_decls.data(), function_param_decls.size()); + } + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, 0, NULL, LLDB_INVALID_UID, Type::eIsTypeWithUID, &decl, clang_type)); + + const_cast(die)->SetUserData(type_sp.get()); + assert(type_sp.get()); + } + } + break; + + case DW_TAG_array_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast(die)->SetUserData(DIE_IS_BEING_PARSED); + + size_t byte_size = 0; + lldb::user_id_t type_die_offset = DW_INVALID_OFFSET; + Declaration decl; + int64_t first_index = 0; + uint32_t byte_stride = 0; + uint32_t bit_stride = 0; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + + if (num_attributes > 0) + { + uint32_t i; + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + + case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; + case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_ordering: + case DW_AT_start_scope: + case DW_AT_visibility: + case DW_AT_specification: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + Type *element_type = ResolveTypeUID(type_die_offset); + + if (element_type) + { + std::vector element_orders; + ParseChildArrayInfo(sc, dwarf_cu, die, first_index, element_orders, byte_stride, bit_stride); + if (byte_stride == 0 && bit_stride == 0) + byte_stride = element_type->GetByteSize(); + void *array_element_type = element_type->GetOpaqueClangQualType(); + uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; + uint64_t num_elements = 0; + std::vector::const_reverse_iterator pos; + std::vector::const_reverse_iterator end = element_orders.rend(); + for (pos = element_orders.rbegin(); pos != end; ++pos) + { + num_elements = *pos; + clang_type = type_list->GetClangASTContext().CreateArrayType (array_element_type, num_elements, num_elements * array_element_bit_stride); + array_element_type = clang_type; + array_element_bit_stride = array_element_bit_stride * num_elements; + } + ConstString empty_name; + type_sp.reset( new Type(die->GetOffset(), this, empty_name, array_element_bit_stride / 8, NULL, LLDB_INVALID_UID, Type::eIsTypeWithUID, &decl, clang_type)); + const_cast(die)->SetUserData(type_sp.get()); + } + } + } + break; + + default: + break; + } + + if (type_sp.get()) + { + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t sc_parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + + SymbolContextScope * symbol_context_scope = NULL; + if (sc_parent_tag == DW_TAG_compile_unit) + { + symbol_context_scope = sc.comp_unit; + } + else if (sc.function != NULL) + { + symbol_context_scope = sc.function->GetBlocks(true).GetBlockByID(sc_parent_die->GetOffset()); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + + if (symbol_context_scope != NULL) + { + type_sp->SetSymbolContextScope(symbol_context_scope); + } + +// if (udt_sp.get()) +// { +// if (is_forward_declaration) +// udt_sp->GetFlags().Set(UserDefType::flagIsForwardDefinition); +// type_sp->SetUserDefinedType(udt_sp); +// } + + if (type_sp.unique()) + { + // We are ready to put this type into the uniqued list up at the module level + TypeSP uniqued_type_sp(m_obj_file->GetModule()->GetTypeList()->InsertUnique(type_sp)); + + const_cast(die)->SetUserData(uniqued_type_sp.get()); + + type_sp = uniqued_type_sp; + } + } + } + else + { + switch (tag) + { + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + case DW_TAG_array_type: + { + Type *existing_type = (Type*)die->GetUserData(); + if (existing_type != DIE_IS_BEING_PARSED) + { + type_sp = m_obj_file->GetModule()->GetTypeList()->FindType(existing_type->GetID()); + } + } + break; + default: + //assert(!"invalid type tag..."); + break; + } + } + } + return type_sp; +} + +size_t +SymbolFileDWARF::ParseTypes (const SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool parse_siblings, bool parse_children) +{ + size_t types_added = 0; + while (die != NULL) + { + bool type_is_new = false; + if (ParseType(sc, dwarf_cu, die, type_is_new).get()) + { + if (type_is_new) + ++types_added; + } + + if (parse_children && die->HasChildren()) + { + if (die->Tag() == DW_TAG_subprogram) + { + SymbolContext child_sc(sc); + child_sc.function = sc.comp_unit->FindFunctionByUID(die->GetOffset()).get(); + types_added += ParseTypes(child_sc, dwarf_cu, die->GetFirstChild(), true, true); + } + else + types_added += ParseTypes(sc, dwarf_cu, die->GetFirstChild(), true, true); + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + return types_added; +} + + +size_t +SymbolFileDWARF::ParseFunctionBlocks (const SymbolContext &sc) +{ + assert(sc.comp_unit && sc.function); + size_t functions_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + dw_offset_t function_die_offset = sc.function->GetID(); + const DWARFDebugInfoEntry *function_die = dwarf_cu->GetDIEPtr(function_die_offset); + if (function_die) + { + ParseFunctionBlocks(sc, Block::RootID, dwarf_cu, function_die, LLDB_INVALID_ADDRESS, false, true); + } + } + + return functions_added; +} + + +size_t +SymbolFileDWARF::ParseTypes (const SymbolContext &sc) +{ + // At least a compile unit must be valid + assert(sc.comp_unit); + size_t types_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + if (sc.function) + { + dw_offset_t function_die_offset = sc.function->GetID(); + const DWARFDebugInfoEntry *func_die = dwarf_cu->GetDIEPtr(function_die_offset); + if (func_die && func_die->HasChildren()) + { + types_added = ParseTypes(sc, dwarf_cu, func_die->GetFirstChild(), true, true); + } + } + else + { + const DWARFDebugInfoEntry *dwarf_cu_die = dwarf_cu->DIE(); + if (dwarf_cu_die && dwarf_cu_die->HasChildren()) + { + types_added = ParseTypes(sc, dwarf_cu, dwarf_cu_die->GetFirstChild(), true, true); + } + } + } + + return types_added; +} + +size_t +SymbolFileDWARF::ParseVariablesForContext (const SymbolContext& sc) +{ + if (sc.comp_unit != NULL) + { + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + + if (dwarf_cu == NULL) + return 0; + + if (sc.function) + { + const DWARFDebugInfoEntry *function_die = dwarf_cu->GetDIEPtr(sc.function->GetID()); + return ParseVariables(sc, dwarf_cu, function_die->GetFirstChild(), true, true); + } + else if (sc.comp_unit) + { + uint32_t vars_added = 0; + VariableListSP variables (sc.comp_unit->GetVariableList(false)); + + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.comp_unit->SetVariableList(variables); + + // Index if we already haven't to make sure the compile units + // get indexed and make their global DIE index list + if (!m_indexed) + Index (); + + const size_t num_globals = dwarf_cu->GetNumGlobals(); + for (size_t idx=0; idxGetGlobalDIEAtIndex (idx))); + if (var_sp) + { + variables->AddVariable(var_sp); + ++vars_added; + } + } + } + return vars_added; + } + } + return 0; +} + + +VariableSP +SymbolFileDWARF::ParseVariableDIE +( + const SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die +) +{ + + VariableSP var_sp; + + const dw_tag_t tag = die->Tag(); + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + const char *name = NULL; + Declaration decl; + uint32_t i; + TypeSP type_sp; + Type *var_type = NULL; + DWARFExpression location; + bool is_external = false; + bool is_artificial = false; + uint32_t accessibility = clang::AS_none; + + for (i=0; iGetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: var_type = GetUniquedTypeForDIEOffset(form_value.Reference(dwarf_cu), type_sp, 0, 0, false); break; + case DW_AT_external: is_external = form_value.Unsigned() != 0; break; + case DW_AT_location: + { + if (form_value.BlockData()) + { + const DataExtractor& debug_info_data = get_debug_info_data(); + + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + location.SetOpcodeData(get_debug_info_data(), block_offset, block_length, NULL); + } + else + { + const DataExtractor& debug_loc_data = get_debug_loc_data(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) + { + Address base_address(dwarf_cu->GetBaseAddress(), m_obj_file->GetSectionList()); + location.SetOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length, &base_address); + } + } + } + break; + + case DW_AT_artificial: is_artificial = form_value.Unsigned() != 0; break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_const_value: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_segment: + case DW_AT_start_scope: + case DW_AT_visibility: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + case DW_AT_specification: + break; + } + } + } + + if (location.IsValid()) + { + assert(var_type != DIE_IS_BEING_PARSED); + + ConstString var_name(name); + + ValueType scope = eValueTypeInvalid; + + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + + if (tag == DW_TAG_formal_parameter) + scope = eValueTypeVariableArgument; + else if (is_external || parent_tag == DW_TAG_compile_unit) + scope = eValueTypeVariableGlobal; + else + scope = eValueTypeVariableLocal; + + SymbolContextScope * symbol_context_scope = NULL; + if (parent_tag == DW_TAG_compile_unit) + { + symbol_context_scope = sc.comp_unit; + } + else if (sc.function != NULL) + { + symbol_context_scope = sc.function->GetBlocks(true).GetBlockByID(sc_parent_die->GetOffset()); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + + assert(symbol_context_scope != NULL); + var_sp.reset (new Variable(die->GetOffset(), + var_name, + var_type, + scope, + symbol_context_scope, + &decl, + location, + is_external, + is_artificial)); + const_cast(die)->SetUserData(var_sp.get()); + } + } + return var_sp; +} + +size_t +SymbolFileDWARF::ParseVariables +( + const SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *orig_die, + bool parse_siblings, + bool parse_children, + VariableList* cc_variable_list +) +{ + if (orig_die == NULL) + return 0; + + size_t vars_added = 0; + const DWARFDebugInfoEntry *die = orig_die; + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(orig_die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + VariableListSP variables; + switch (parent_tag) + { + case DW_TAG_compile_unit: + if (sc.comp_unit != NULL) + { + variables = sc.comp_unit->GetVariableList(false); + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.comp_unit->SetVariableList(variables); + } + } + else + { + assert(!"Parent DIE was a compile unit, yet we don't have a valid compile unit in the symbol context..."); + vars_added = 0; + } + break; + + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function != NULL) + { + // Check to see if we already have parsed the variables for the given scope + variables = sc.function->GetBlocks(true).GetVariableList(sc_parent_die->GetOffset(), false, false); + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.function->GetBlocks(true).SetVariableList(sc_parent_die->GetOffset(), variables); + } + } + else + { + assert(!"Parent DIE was a function or block, yet we don't have a function in the symbol context..."); + vars_added = 0; + } + break; + + default: + assert(!"Didn't find appropriate parent DIE for variable list..."); + break; + } + + // We need to have a variable list at this point that we can add variables to + assert(variables.get()); + + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + // Check to see if we have already parsed this variable or constant? + if (die->GetUserData() == NULL) + { + // We haven't already parsed it, lets do that now. + if ((tag == DW_TAG_variable) || + (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter && sc.function)) + { + VariableSP var_sp (ParseVariableDIE(sc, dwarf_cu, die)); + if (var_sp) + { + variables->AddVariable(var_sp); + ++vars_added; + } + } + } + + bool skip_children = (sc.function == NULL && tag == DW_TAG_subprogram); + + if (!skip_children && parse_children && die->HasChildren()) + { + vars_added += ParseVariables(sc, dwarf_cu, die->GetFirstChild(), true, true); + //vars_added += ParseVariables(sc, dwarf_cu, die->GetFirstChild(), parse_siblings, parse_children); + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + + if (cc_variable_list) + { + cc_variable_list->AddVariables(variables.get()); + } + + return vars_added; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolFileDWARF::GetPluginName() +{ + return "SymbolFileDWARF"; +} + +const char * +SymbolFileDWARF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileDWARF::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileDWARF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolFileDWARF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolFileDWARF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h new file mode 100644 index 000000000000..95545a4844ee --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -0,0 +1,331 @@ +//===-- SymbolFileDWARF.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolFileDWARF_h_ +#define liblldb_SymbolFileDWARF_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include + +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolContext.h" + +// Project includes +#include "DWARFDefines.h" + + +//---------------------------------------------------------------------- +// Forward Declarations for this DWARF plugin +//---------------------------------------------------------------------- +class DWARFAbbreviationDeclaration; +class DWARFAbbreviationDeclarationSet; +class DWARFCompileUnit; +class DWARFDebugAbbrev; +class DWARFDebugAranges; +class DWARFDebugInfo; +class DWARFDebugInfoEntry; +class DWARFDebugLine; +class DWARFDebugPubnames; +class DWARFDebugRanges; +class DWARFDIECollection; +class DWARFFormValue; + +class SymbolFileDWARF : public lldb_private::SymbolFile +{ +public: + friend class SymbolFileDWARFDebugMap; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile* + CreateInstance (lldb_private::ObjectFile* obj_file); + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileDWARF(lldb_private::ObjectFile* ofile); + virtual ~SymbolFileDWARF(); + + virtual uint32_t GetAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t GetNumCompileUnits(); + virtual lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index); + + virtual size_t ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList& support_files); + virtual size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + virtual size_t ParseTypes (const lldb_private::SymbolContext& sc); + virtual size_t ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* ResolveTypeUID(lldb::user_id_t type_uid); + virtual clang::DeclContext* GetClangDeclContextForTypeUID (lldb::user_id_t type_uid); + + virtual uint32_t ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + virtual uint32_t ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindGlobalVariables(const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindGlobalVariables(const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindFunctions(const lldb_private::ConstString &name, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindFunctions(const lldb_private::RegularExpression& regex, bool append, lldb_private::SymbolContextList& sc_list); +// virtual uint32_t FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); +// virtual uint32_t FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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); + + // Approach 2 - count + accessor + // Index compile units would scan the initial compile units and register + // them with the module. This would only be done on demand if and only if + // the compile units were needed. + //virtual size_t GetCompUnitCount() = 0; + //virtual CompUnitSP GetCompUnitAtIndex(size_t cu_idx) = 0; + + const lldb_private::DataExtractor& get_debug_abbrev_data(); + const lldb_private::DataExtractor& get_debug_aranges_data(); + const lldb_private::DataExtractor& get_debug_frame_data(); + const lldb_private::DataExtractor& get_debug_info_data(); + const lldb_private::DataExtractor& get_debug_line_data(); + const lldb_private::DataExtractor& get_debug_loc_data(); + const lldb_private::DataExtractor& get_debug_macinfo_data(); + const lldb_private::DataExtractor& get_debug_pubnames_data(); + const lldb_private::DataExtractor& get_debug_pubtypes_data(); + const lldb_private::DataExtractor& get_debug_ranges_data(); + const lldb_private::DataExtractor& get_debug_str_data(); + + DWARFDebugAbbrev* DebugAbbrev(); + const DWARFDebugAbbrev* DebugAbbrev() const; + + DWARFDebugAranges* DebugAranges(); + const DWARFDebugAranges*DebugAranges() const; + + DWARFDebugInfo* DebugInfo(); + const DWARFDebugInfo* DebugInfo() const; + +// These shouldn't be used unless we want to dump the DWARF line tables. +// DWARFDebugLine* DebugLine(); +// const DWARFDebugLine* DebugLine() const; + +// DWARFDebugPubnames* DebugPubnames(); +// const DWARFDebugPubnames* DebugPubnames() const; +// +// DWARFDebugPubnames* DebugPubBaseTypes(); +// const DWARFDebugPubnames* DebugPubBaseTypes() const; +// +// DWARFDebugPubnames* DebugPubtypes(); +// const DWARFDebugPubnames* DebugPubtypes() const; + + DWARFDebugRanges* DebugRanges(); + const DWARFDebugRanges* DebugRanges() const; + + const lldb_private::DataExtractor& + GetCachedSectionData (uint32_t got_flag, const lldb_private::ConstString §ion_name, lldb_private::DataExtractor &data); + + static bool SupportedVersion(uint16_t version); + + clang::DeclContext * + GetClangDeclContextForDIE (const DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die); + + clang::DeclContext * + GetClangDeclContextForDIEOffset (dw_offset_t die_offset); + + lldb_private::Flags& + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags& + GetFlags () const + { + return m_flags; + } + +protected: + + enum + { + flagsGotDebugAbbrevData = (1 << 0), + flagsGotDebugArangesData = (1 << 1), + flagsGotDebugFrameData = (1 << 2), + flagsGotDebugInfoData = (1 << 3), + flagsGotDebugLineData = (1 << 4), + flagsGotDebugLocData = (1 << 5), + flagsGotDebugMacInfoData = (1 << 6), + flagsGotDebugPubNamesData = (1 << 7), + flagsGotDebugPubTypesData = (1 << 8), + flagsGotDebugRangesData = (1 << 9), + flagsGotDebugStrData = (1 << 10), + // True if this is a .o file used when resolving a N_OSO entry with + // debug maps. + flagsDWARFIsOSOForDebugMap = (1 << 16) + }; + + DISALLOW_COPY_AND_ASSIGN (SymbolFileDWARF); + bool ParseCompileUnit(DWARFCompileUnit* cu, lldb::CompUnitSP& compile_unit_sp); + DWARFCompileUnit* GetDWARFCompileUnitForUID(lldb::user_id_t cu_uid); + DWARFCompileUnit* GetNextUnparsedDWARFCompileUnit(DWARFCompileUnit* prev_cu); + lldb_private::CompileUnit* GetCompUnitForDWARFCompUnit(DWARFCompileUnit* cu, uint32_t cu_idx = UINT_MAX); + bool GetFunction (DWARFCompileUnit* cu, const DWARFDebugInfoEntry* func_die, lldb_private::SymbolContext& sc); + lldb_private::Function * ParseCompileUnitFunction (const lldb_private::SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die); + size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc, + lldb::user_id_t parentBlockID, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + lldb::addr_t subprogram_low_pc, + bool parse_siblings, + bool parse_children); + size_t ParseTypes (const lldb_private::SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool parse_siblings, bool parse_children); + lldb::TypeSP ParseType (const lldb_private::SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool &type_is_new); + + lldb::VariableSP ParseVariableDIE( + const lldb_private::SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die); + + size_t ParseVariables( + const lldb_private::SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + bool parse_siblings, + bool parse_children, + lldb_private::VariableList* cc_variable_list = NULL); + + size_t ParseChildMembers( + const lldb_private::SymbolContext& sc, + lldb::TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + std::vector& base_classes, + std::vector& member_accessibilities, + int &default_accessibility, + bool &is_a_class); + + size_t ParseChildParameters( + const lldb_private::SymbolContext& sc, + lldb::TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + lldb_private::TypeList* type_list, + std::vector& function_args, + std::vector& function_param_decls); + + size_t ParseChildEnumerators( + const lldb_private::SymbolContext& sc, + lldb::TypeSP& type_sp, + void *enumerator_qual_type, + uint32_t enumerator_byte_size, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *enum_die); + + void ParseChildArrayInfo( + const lldb_private::SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + int64_t& first_index, + std::vector& element_orders, + uint32_t& byte_stride, + uint32_t& bit_stride); + + lldb_private::Type* GetUniquedTypeForDIEOffset(dw_offset_t type_die_offset, lldb::TypeSP& owning_type_sp, int32_t child_type, uint32_t idx, bool safe); + lldb::TypeSP GetTypeForDIE(DWARFCompileUnit *cu, const DWARFDebugInfoEntry* die, lldb::TypeSP& owning_type_sp, int32_t child_type, uint32_t idx); +// uint32_t FindTypes(std::vector die_offsets, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types); + + void Index(); + + lldb_private::Flags m_flags; + lldb_private::DataExtractor m_dwarf_data; + lldb_private::DataExtractor m_data_debug_abbrev; + lldb_private::DataExtractor m_data_debug_aranges; + lldb_private::DataExtractor m_data_debug_frame; + lldb_private::DataExtractor m_data_debug_info; + lldb_private::DataExtractor m_data_debug_line; + lldb_private::DataExtractor m_data_debug_loc; + lldb_private::DataExtractor m_data_debug_macinfo; + lldb_private::DataExtractor m_data_debug_pubnames; + lldb_private::DataExtractor m_data_debug_pubtypes; + lldb_private::DataExtractor m_data_debug_ranges; + lldb_private::DataExtractor m_data_debug_str; + + // The auto_ptr items below are generated on demand if and when someone accesses + // them through a non const version of this class. + std::auto_ptr m_abbr; + std::auto_ptr m_aranges; + std::auto_ptr m_info; + std::auto_ptr m_line; + lldb_private::UniqueCStringMap m_name_to_function_die; // All concrete functions + lldb_private::UniqueCStringMap m_name_to_inlined_die; // All inlined functions + lldb_private::UniqueCStringMap m_name_to_global_die; // Global and static variables + lldb_private::UniqueCStringMap m_name_to_type_die; // All type DIE offsets + bool m_indexed; + +// std::auto_ptr m_pubnames; +// std::auto_ptr m_pubbasetypes; // Just like m_pubtypes, but for DW_TAG_base_type DIEs +// std::auto_ptr m_pubtypes; + std::auto_ptr m_ranges; + + typedef llvm::DenseMap DIEToDeclContextMap; + DIEToDeclContextMap m_die_to_decl_ctx; + +// TypeFixupColl m_type_fixups; +// std::vector m_indirect_fixups; + +//#define LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST 1 +#if defined(LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST) + + typedef std::map FSToDIES; + void ShrinkDSYM(CompileUnit *dc_cu, DWARFCompileUnit *dw_cu, const FileSpec& cu_fspec, const FileSpec& base_types_cu_fspec, FSToDIES& fs_to_dies, const DWARFDebugInfoEntry *die); +#endif +}; + +#endif // liblldb_SymbolFileDWARF_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp new file mode 100644 index 000000000000..7bf968dd9ce6 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -0,0 +1,873 @@ +//===-- SymbolFileDWARFDebugMap.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARFDebugMap.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; + +void +SymbolFileDWARFDebugMap::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileDWARFDebugMap::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +SymbolFileDWARFDebugMap::GetPluginNameStatic() +{ + return "symbol-file.dwarf2-debugmap"; +} + +const char * +SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() +{ + return "DWARF and DWARF3 debug symbol file reader (debug map)."; +} + +SymbolFile* +SymbolFileDWARFDebugMap::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileDWARFDebugMap (obj_file); +} + + +SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap (ObjectFile* ofile) : + SymbolFile(ofile), + m_flags(), + m_compile_unit_infos(), + m_func_indexes(), + m_glob_indexes() +{ +} + + +SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() +{ +} + +void +SymbolFileDWARFDebugMap::InitOSO () +{ + if (m_flags.test(kHaveInitializedOSOs)) + return; + + m_flags.set(kHaveInitializedOSOs); + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that + // then we return the abilities of the first N_OSO's DWARF. + + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + //StreamFile s(0, 4, eByteOrderHost, stdout); + std::vector oso_indexes; + const uint32_t oso_index_count = symtab->AppendSymbolIndexesWithType(eSymbolTypeObjectFile, oso_indexes); + + symtab->AppendSymbolIndexesWithType(eSymbolTypeFunction, m_func_indexes); + symtab->AppendSymbolIndexesWithType(eSymbolTypeGlobal, m_glob_indexes); + + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + symtab->SortSymbolIndexesByValue(m_glob_indexes, true); + + if (oso_index_count > 0) + { + m_compile_unit_infos.resize(oso_index_count); +// s.Printf("%s N_OSO symbols:\n", __PRETTY_FUNCTION__); +// symtab->Dump(&s, oso_indexes); + + for (uint32_t i=0; iSymbolAtIndex(oso_indexes[i] - 1); + if (m_compile_unit_infos[i].so_symbol->GetSiblingIndex() == 0) + m_compile_unit_infos[i].so_symbol = symtab->SymbolAtIndex(oso_indexes[i] - 2); + m_compile_unit_infos[i].oso_symbol = symtab->SymbolAtIndex(oso_indexes[i]); + } + } + } +} + +Module * +SymbolFileDWARFDebugMap::GetModuleByOSOIndex (uint32_t oso_idx) +{ + const uint32_t cu_count = GetNumCompileUnits(); + if (oso_idx < cu_count) + return GetModuleByCompUnitInfo (&m_compile_unit_infos[oso_idx]); + return NULL; +} + +Module * +SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + if (comp_unit_info->oso_module_sp.get() == NULL) + { + Symbol *oso_symbol = comp_unit_info->oso_symbol; + if (oso_symbol) + { + FileSpec oso_file_spec(oso_symbol->GetMangled().GetName().AsCString()); + + ModuleList::GetSharedModule (oso_file_spec, + m_obj_file->GetModule()->GetArchitecture(), + NULL, // UUID pointer + NULL, // object name + 0, // object offset + comp_unit_info->oso_module_sp, + NULL, + NULL); + //comp_unit_info->oso_module_sp.reset(new Module (oso_file_spec, m_obj_file->GetModule()->GetArchitecture())); + } + } + return comp_unit_info->oso_module_sp.get(); +} + + +bool +SymbolFileDWARFDebugMap::GetFileSpecForSO (uint32_t oso_idx, FileSpec &file_spec) +{ + if (oso_idx < m_compile_unit_infos.size()) + { + if (!m_compile_unit_infos[oso_idx].so_file) + { + + if (m_compile_unit_infos[oso_idx].so_symbol == NULL) + return false; + + std::string so_path (m_compile_unit_infos[oso_idx].so_symbol->GetMangled().GetName().AsCString()); + if (m_compile_unit_infos[oso_idx].so_symbol[1].GetType() == eSymbolTypeSourceFile) + so_path += m_compile_unit_infos[oso_idx].so_symbol[1].GetMangled().GetName().AsCString(); + m_compile_unit_infos[oso_idx].so_file.SetFile(so_path.c_str()); + } + file_spec = m_compile_unit_infos[oso_idx].so_file; + return true; + } + return false; +} + + + +ObjectFile * +SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex (uint32_t oso_idx) +{ + Module *oso_module = GetModuleByOSOIndex (oso_idx); + if (oso_module) + return oso_module->GetObjectFile(); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFile (const SymbolContext& sc) +{ + CompileUnitInfo *comp_unit_info = GetCompUnitInfo (sc); + if (comp_unit_info) + return GetSymbolFileByCompUnitInfo (comp_unit_info); + return NULL; +} + +ObjectFile * +SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + Module *oso_module = GetModuleByCompUnitInfo (comp_unit_info); + if (oso_module) + return oso_module->GetObjectFile(); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex (uint32_t oso_idx) +{ + if (oso_idx < m_compile_unit_infos.size()) + return GetSymbolFileByCompUnitInfo (&m_compile_unit_infos[oso_idx]); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + if (comp_unit_info->oso_symbol_vendor == NULL) + { + ObjectFile *oso_objfile = GetObjectFileByCompUnitInfo (comp_unit_info); + + if (oso_objfile) + { + comp_unit_info->oso_symbol_vendor = oso_objfile->GetModule()->GetSymbolVendor(); +// SymbolFileDWARF *oso_dwarf = new SymbolFileDWARF(oso_objfile); +// comp_unit_info->oso_dwarf_sp.reset (oso_dwarf); + if (comp_unit_info->oso_symbol_vendor) + { + // Set a bit that lets this DWARF file know that it is being + // used along with a debug map and that it will have the + // remapped sections that we do below. + ((SymbolFileDWARF *)comp_unit_info->oso_symbol_vendor->GetSymbolFile())->GetFlags().Set(SymbolFileDWARF::flagsDWARFIsOSOForDebugMap); + comp_unit_info->debug_map_sections_sp.reset(new SectionList); + + Symtab *exe_symtab = m_obj_file->GetSymtab(); + Module *oso_module = oso_objfile->GetModule(); + Symtab *oso_symtab = oso_objfile->GetSymtab(); +//#define DEBUG_OSO_DMAP // Do not check in with this defined... +#if defined(DEBUG_OSO_DMAP) + StreamFile s(stdout); + s << "OSO symtab:\n"; + oso_symtab->Dump(&s, NULL); + s << "OSO sections before:\n"; + oso_objfile->GetSectionList()->Dump(&s, NULL, true); +#endif + + ///const uint32_t fun_resolve_flags = SymbolContext::Module | eSymbolContextCompUnit | eSymbolContextFunction; + //SectionList *oso_sections = oso_objfile->Sections(); + // Now we need to make sections that map from zero based object + // file addresses to where things eneded up in the main executable. + uint32_t oso_start_idx = comp_unit_info->oso_symbol->GetID() + 1; + const uint32_t oso_end_idx = comp_unit_info->so_symbol->GetSiblingIndex(); + uint32_t sect_id = 0x10000; + for (uint32_t idx = oso_start_idx; idx < oso_end_idx; ++idx) + { + Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx); + if (exe_symbol) + { + switch (exe_symbol->GetType()) + { + case eSymbolTypeFunction: + { + // For each N_FUN, or function that we run into in the debug map + // we make a new section that we add to the sections found in the + // .o file. This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. + + // First we find the original symbol in the .o file's symbol table + Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeCode); + if (oso_fun_symbol) + { + // If we found the symbol, then we + Section* exe_fun_section = const_cast

(exe_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + Section* oso_fun_section = const_cast
(oso_fun_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + if (oso_fun_section) + { + // Now we create a section that we will add as a child of the + // section in which the .o symbol (the N_FUN) exists. + + // We use the exe_symbol size because the one in the .o file + // will just be a symbol with no size, and the exe_symbol + // size will reflect any size changes (ppc has been known to + // shrink function sizes when it gets rid of jump islands that + // aren't needed anymore). + SectionSP oso_fun_section_sp (new Section (const_cast
(oso_fun_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()), + oso_module, // Module (the .o file) + sect_id++, // Section ID starts at 0x10000 and increments so the section IDs don't overlap with the standard mach IDs + exe_symbol->GetMangled().GetName(), // Name the section the same as the symbol for which is was generated! + eSectionTypeDebug, + oso_fun_symbol->GetAddressRangePtr()->GetBaseAddress().GetOffset(), // File VM address offset in the current section + exe_symbol->GetByteSize(), // File size (we need the size from the executable) + 0, 0, 0)); + + oso_fun_section_sp->SetLinkedLocation (exe_fun_section, + exe_symbol->GetValue().GetFileAddress() - exe_fun_section->GetFileAddress()); + oso_fun_section->GetChildren().AddSection(oso_fun_section_sp); + comp_unit_info->debug_map_sections_sp->AddSection(oso_fun_section_sp); + } + } + } + break; + + case eSymbolTypeGlobal: + case eSymbolTypeStatic: + { + // For each N_GSYM we remap the address for the global by making + // a new section that we add to the sections found in the .o file. + // This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. We + // initially set the section size to be 1 byte, but will need to + // fix up these addresses further after all globals have been + // parsed to span the gaps, or we can find the global variable + // sizes from the DWARF info as we are parsing. + +#if 0 + // First we find the non-stab entry that corresponds to the N_GSYM in the executable + Symbol *exe_gsym_symbol = exe_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeData); +#else + // The mach-o object file parser already matches up the N_GSYM with with the non-stab + // entry, so we shouldn't have to do that. If this ever changes, enable the code above + // in the "#if 0" block. STSYM's always match the symbol as found below. + Symbol *exe_gsym_symbol = exe_symbol; +#endif + // Next we find the non-stab entry that corresponds to the N_GSYM in the .o file + Symbol *oso_gsym_symbol = oso_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeData); + if (exe_gsym_symbol && oso_gsym_symbol) + { + // If we found the symbol, then we + Section* exe_gsym_section = const_cast
(exe_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + Section* oso_gsym_section = const_cast
(oso_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + if (oso_gsym_section) + { + SectionSP oso_gsym_section_sp (new Section (const_cast
(oso_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()), + oso_module, // Module (the .o file) + sect_id++, // Section ID starts at 0x10000 and increments so the section IDs don't overlap with the standard mach IDs + exe_symbol->GetMangled().GetName(), // Name the section the same as the symbol for which is was generated! + eSectionTypeDebug, + oso_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetOffset(), // File VM address offset in the current section + 1, // We don't know the size of the global, just do the main address for now. + 0, 0, 0)); + + oso_gsym_section_sp->SetLinkedLocation (exe_gsym_section, + exe_gsym_symbol->GetValue().GetFileAddress() - exe_gsym_section->GetFileAddress()); + oso_gsym_section->GetChildren().AddSection(oso_gsym_section_sp); + comp_unit_info->debug_map_sections_sp->AddSection(oso_gsym_section_sp); + } + } + } + break; + +// case eSymbolTypeStatic: +// { +// // For each N_STSYM we remap the address for the global by making +// // a new section that we add to the sections found in the .o file. +// // This new section has the file address set to what the +// // addresses are in the .o file, and the load address is adjusted +// // to match where it ended up in the final executable! We do this +// // before we parse any dwarf info so that when it goes get parsed +// // all section/offset addresses that get registered will resolve +// // correctly to the new addresses in the main executable. We +// // initially set the section size to be 1 byte, but will need to +// // fix up these addresses further after all globals have been +// // parsed to span the gaps, or we can find the global variable +// // sizes from the DWARF info as we are parsing. +// +// +// Symbol *exe_stsym_symbol = exe_symbol; +// // First we find the non-stab entry that corresponds to the N_STSYM in the .o file +// Symbol *oso_stsym_symbol = oso_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeData); +// if (exe_stsym_symbol && oso_stsym_symbol) +// { +// // If we found the symbol, then we +// Section* exe_stsym_section = const_cast
(exe_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); +// Section* oso_stsym_section = const_cast
(oso_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); +// if (oso_stsym_section) +// { +// // The load address of the symbol will use the section in the +// // executable that contains the debug map that corresponds to +// // the N_FUN symbol. We set the offset to reflect the offset +// // into that section since we are creating a new section. +// AddressRange stsym_load_range(exe_stsym_section, exe_stsym_symbol->GetValue().GetFileAddress() - exe_stsym_section->GetFileAddress(), 1); +// // We need the symbol's section offset address from the .o file, but +// // we need a non-zero size. +// AddressRange stsym_file_range(exe_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection(), exe_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetOffset(), 1); +// +// // Now we create a section that we will add as a child of the +// // section in which the .o symbol (the N_FUN) exists. +// +//// TODO: mimic what I did for N_FUN if that works... +//// // We use the 1 byte for the size because we don't know the +//// // size of the global symbol without seeing the DWARF. +//// SectionSP oso_fun_section_sp (new Section ( NULL, oso_module, // Module (the .o file) +//// sect_id++, // Section ID starts at 0x10000 and increments so the section IDs don't overlap with the standard mach IDs +//// exe_symbol->GetMangled().GetName(),// Name the section the same as the symbol for which is was generated! +//// // &stsym_load_range, // Load offset is the offset into the executable section for the N_FUN from the debug map +//// &stsym_file_range, // File section/offset is just the same os the symbol on the .o file +//// 0, 0, 0)); +//// +//// // Now we add the new section to the .o file's sections as a child +//// // of the section in which the N_SECT symbol exists. +//// oso_stsym_section->GetChildren().AddSection(oso_fun_section_sp); +//// comp_unit_info->debug_map_sections_sp->AddSection(oso_fun_section_sp); +// } +// } +// } +// break; + } + } + } +#if defined(DEBUG_OSO_DMAP) + s << "OSO sections after:\n"; + oso_objfile->GetSectionList()->Dump(&s, NULL, true); +#endif + } + } + } + if (comp_unit_info->oso_symbol_vendor) + return (SymbolFileDWARF *)comp_unit_info->oso_symbol_vendor->GetSymbolFile(); + return NULL; +} + +uint32_t +SymbolFileDWARFDebugMap::GetAbilities () +{ + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that + // then we return the abilities of the first N_OSO's DWARF. + + const uint32_t oso_index_count = GetNumCompileUnits(); + if (oso_index_count > 0) + { + const uint32_t dwarf_abilities = SymbolFile::CompileUnits | + SymbolFile::Functions | + SymbolFile::Blocks | + SymbolFile::GlobalVariables | + SymbolFile::LocalVariables | + SymbolFile::VariableTypes | + SymbolFile::LineTables; + + for (uint32_t oso_idx=0; oso_idxGetAbilities(); + if ((oso_abilities & dwarf_abilities) == dwarf_abilities) + return oso_abilities; + } + } + } + return 0; +} + +uint32_t +SymbolFileDWARFDebugMap::GetNumCompileUnits() +{ + InitOSO (); + return m_compile_unit_infos.size(); +} + + +CompUnitSP +SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) +{ + CompUnitSP comp_unit_sp; + const uint32_t cu_count = GetNumCompileUnits(); + + if (cu_idx < cu_count) + { + if (m_compile_unit_infos[cu_idx].oso_compile_unit_sp.get() == NULL) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (cu_idx); + if (oso_dwarf) + { + // There is only one compile unit for N_OSO entry right now, so + // it will always exist at index zero. + m_compile_unit_infos[cu_idx].oso_compile_unit_sp = m_compile_unit_infos[cu_idx].oso_symbol_vendor->GetCompileUnitAtIndex (0); + } + + if (m_compile_unit_infos[cu_idx].oso_compile_unit_sp.get() == NULL) + { + // We weren't able to get the DWARF for this N_OSO entry (the + // .o file may be missing or not at the specified path), make + // one up as best we can from the debug map. We set the uid + // of the compile unit to the symbol index with the MSBit set + // so that it doesn't collide with any uid values from the DWARF + Symbol *so_symbol = m_compile_unit_infos[cu_idx].so_symbol; + if (so_symbol) + { + m_compile_unit_infos[cu_idx].oso_compile_unit_sp.reset(new CompileUnit (m_obj_file->GetModule(), + NULL, + so_symbol->GetMangled().GetName().AsCString(), + cu_idx, + Language::Unknown)); + } + } + } + comp_unit_sp = m_compile_unit_infos[cu_idx].oso_compile_unit_sp; + } + + return comp_unit_sp; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompUnitInfo (const SymbolContext& sc) +{ + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t i=0; iParseCompileUnitFunctions (sc); + return 0; +} + +bool +SymbolFileDWARFDebugMap::ParseCompileUnitLineTable (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitLineTable (sc); + return false; +} + +bool +SymbolFileDWARFDebugMap::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitSupportFiles (sc, support_files); + return false; +} + + +size_t +SymbolFileDWARFDebugMap::ParseFunctionBlocks (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseFunctionBlocks (sc); + return 0; +} + + +size_t +SymbolFileDWARFDebugMap::ParseTypes (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseTypes (sc); + return 0; +} + + +size_t +SymbolFileDWARFDebugMap::ParseVariablesForContext (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseTypes (sc); + return 0; +} + + + +Type* +SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) +{ + return NULL; +} + + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext (const Address& exe_so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + uint32_t resolved_flags = 0; + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + const addr_t exe_file_addr = exe_so_addr.GetFileAddress(); + sc.symbol = symtab->FindSymbolContainingFileAddress (exe_file_addr, &m_func_indexes[0], m_func_indexes.size()); + + if (sc.symbol != NULL) + { + resolved_flags |= eSymbolContextSymbol; + + uint32_t oso_idx = 0; + CompileUnitInfo* comp_unit_info = GetCompileUnitInfoForSymbolWithIndex (sc.symbol->GetID(), &oso_idx); + if (comp_unit_info) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + ObjectFile *oso_objfile = GetObjectFileByOSOIndex (oso_idx); + if (oso_dwarf && oso_objfile) + { + SectionList *oso_section_list = oso_objfile->GetSectionList(); + + + SectionSP oso_section_sp(oso_section_list->FindSectionByName(exe_so_addr.GetSection()->GetName())); + if (oso_section_sp) + { + SectionSP oso_symbol_section_sp (oso_section_sp->GetChildren().FindSectionContainingLinkedFileAddress (exe_file_addr)); + + if (oso_symbol_section_sp) + { + const addr_t linked_file_addr = oso_symbol_section_sp->GetLinkedFileAddress(); + Address oso_so_addr (oso_symbol_section_sp.get(), exe_file_addr - linked_file_addr); + if (oso_so_addr.IsSectionOffset()) + resolved_flags |= oso_dwarf->ResolveSymbolContext (oso_so_addr, resolve_scope, sc); + } + } + // Map the load address from in the executable back to a + // section/offset address in the .o file so we can do + // lookups in the .o DWARF. +// Address oso_so_addr (exe_load_addr, false, comp_unit_info->debug_map_sections_sp.get()); +// +// // Make sure we were able to resolve this back to a .o +// // section offset address, and if so, resolve the context +// // for everything that was asked for. +// if (oso_so_addr.IsSectionOffset()) +// resolved_flags |= oso_dwarf->ResolveSymbolContext (oso_so_addr, resolve_scope, sc); + } + } + } + } + return resolved_flags; +} + + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + uint32_t initial = sc_list.GetSize(); + const uint32_t cu_count = GetNumCompileUnits(); + + FileSpec so_file_spec; + for (uint32_t i=0; iResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); + } + } + } + return sc_list.GetSize() - initial; +} + +uint32_t +SymbolFileDWARFDebugMap::PrivateFindGlobalVariables +( + const ConstString &name, + const std::vector &indexes, // Indexes into the symbol table that match "name" + uint32_t max_matches, + VariableList& variables +) +{ + const uint32_t original_size = variables.GetSize(); + const size_t match_count = indexes.size(); + for (size_t i=0; iFindGlobalVariables(name, true, max_matches, variables)) + if (variables.GetSize() > max_matches) + break; + } + } + } + return variables.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + std::vector indexes; + const size_t match_count = m_obj_file->GetSymtab()->FindAllSymbolsWithNameAndType (name, eSymbolTypeGlobal, indexes); + if (match_count) + { + PrivateFindGlobalVariables (name, indexes, max_matches, variables); + } + } + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + + +int +SymbolFileDWARFDebugMap::SymbolContainsSymbolIndex (uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) +{ + const uint32_t symbol_idx = *symbol_idx_ptr; + + if (symbol_idx < comp_unit_info->so_symbol->GetID()) + return -1; + + if (symbol_idx < comp_unit_info->so_symbol->GetSiblingIndex()) + return 0; + + return 1; +} + + +SymbolFileDWARFDebugMap::CompileUnitInfo* +SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex (uint32_t symbol_idx, uint32_t *oso_idx_ptr) +{ + const uint32_t oso_index_count = m_compile_unit_infos.size(); + CompileUnitInfo *comp_unit_info = NULL; + if (oso_index_count) + { + comp_unit_info = (CompileUnitInfo*)bsearch(&symbol_idx, &m_compile_unit_infos[0], m_compile_unit_infos.size(), sizeof(CompileUnitInfo), (comparison_function)SymbolContainsSymbolIndex); + } + + if (oso_idx_ptr) + { + if (comp_unit_info != NULL) + *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; + else + *oso_idx_ptr = UINT32_MAX; + } + return comp_unit_info; +} + +uint32_t +SymbolFileDWARFDebugMap::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::FindFunctions (name = %s)", + name.GetCString()); + + + std::vector indexes; + uint32_t initial_size = 0; + if (append) + initial_size = sc_list.GetSize(); + else + sc_list.Clear(); + + const size_t match_count = m_obj_file->GetSymtab()->FindAllSymbolsWithNameAndType (name, eSymbolTypeFunction, indexes); + if (match_count > 0) + { + for (size_t i=0; iFindFunctions(name, true, sc_list); + } + } +// Stream s(stdout); +// sc_list.Dump(&s); + } + + return sc_list.GetSize() - initial_size; +} + + +uint32_t +SymbolFileDWARFDebugMap::FindFunctions (const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')", + regex.GetText()); + + + return 0; +} + +// +//uint32_t +//SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); +// if (oso_dwarf) +// return oso_dwarf->FindTypes (sc, name, append, max_matches, encoding, udt_uid, types); +// return 0; +//} +// +// +//uint32_t +//SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); +// if (oso_dwarf) +// return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding, udt_uid, types); +// return 0; +//} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolFileDWARFDebugMap::GetPluginName() +{ + return "SymbolFileDWARFDebugMap"; +} + +const char * +SymbolFileDWARFDebugMap::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileDWARFDebugMap::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileDWARFDebugMap::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolFileDWARFDebugMap::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolFileDWARFDebugMap::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h new file mode 100644 index 000000000000..0a312ab9f266 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -0,0 +1,186 @@ +//===-- SymbolFileDWARFDebugMap.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolFileDWARFDebugMap_h_ +#define liblldb_SymbolFileDWARFDebugMap_h_ + + +#include +#include +#include "lldb/Symbol/SymbolFile.h" + +class SymbolFileDWARF; + +class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance (lldb_private::ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileDWARFDebugMap (lldb_private::ObjectFile* ofile); + virtual ~ SymbolFileDWARFDebugMap (); + + virtual uint32_t GetAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t GetNumCompileUnits (); + virtual lldb::CompUnitSP ParseCompileUnitAtIndex (uint32_t index); + + virtual size_t ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList &support_files); + virtual size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + virtual size_t ParseTypes (const lldb_private::SymbolContext& sc); + virtual size_t ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* ResolveTypeUID (lldb::user_id_t type_uid); + virtual uint32_t ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + virtual uint32_t ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindGlobalVariables (const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindGlobalVariables (const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindFunctions (const lldb_private::ConstString &name, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindFunctions (const lldb_private::RegularExpression& regex, bool append, lldb_private::SymbolContextList& sc_list); +// virtual uint32_t FindTypes (const lldb_private::SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types); +// virtual uint32_t FindTypes (const lldb_private::SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + enum + { + kHaveInitializedOSOs = (1 << 0), + kNumFlags + }; + + //------------------------------------------------------------------ + // Class specific types + //------------------------------------------------------------------ + struct CompileUnitInfo + { + lldb_private::FileSpec so_file; + lldb_private::Symbol *so_symbol; + lldb_private::Symbol *oso_symbol; + lldb::ModuleSP oso_module_sp; + lldb::CompUnitSP oso_compile_unit_sp; + lldb_private::SymbolVendor *oso_symbol_vendor; +// lldb_private::shared_ptr oso_dwarf_sp; +// lldb_private::shared_ptr oso_dwarf_sp; + std::vector function_indexes; + std::vector static_indexes; + lldb::SharedPtr::Type debug_map_sections_sp; + + CompileUnitInfo() : + so_file(), + so_symbol(NULL), + oso_symbol(NULL), + oso_module_sp(), + oso_compile_unit_sp(), + oso_symbol_vendor(NULL), +// oso_dwarf_sp(), + function_indexes(), + static_indexes(), + debug_map_sections_sp() + { + } + }; + + //------------------------------------------------------------------ + // Protected Member Functions + //------------------------------------------------------------------ + void + InitOSO (); + + bool + GetFileSpecForSO (uint32_t oso_idx, lldb_private::FileSpec &file_spec); + + CompileUnitInfo * + GetCompUnitInfo (const lldb_private::SymbolContext& sc); + + lldb_private::Module * + GetModuleByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + lldb_private::Module * + GetModuleByOSOIndex (uint32_t oso_idx); + + lldb_private::ObjectFile * + GetObjectFileByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + lldb_private::ObjectFile * + GetObjectFileByOSOIndex (uint32_t oso_idx); + + SymbolFileDWARF * + GetSymbolFile (const lldb_private::SymbolContext& sc); + + SymbolFileDWARF * + GetSymbolFileByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF * + GetSymbolFileByOSOIndex (uint32_t oso_idx); + + CompileUnitInfo* + GetCompileUnitInfoForSymbolWithIndex (uint32_t symbol_idx, uint32_t *oso_idx_ptr); + + static int + SymbolContainsSymbolIndex (uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info); + + uint32_t + PrivateFindGlobalVariables (const lldb_private::ConstString &name, + const std::vector &name_symbol_indexes, + uint32_t max_matches, + lldb_private::VariableList& variables); + + //------------------------------------------------------------------ + // Member Variables + //------------------------------------------------------------------ + std::bitset m_flags; + std::vector m_compile_unit_infos; + std::vector m_func_indexes; // Sorted by address + std::vector m_glob_indexes; +}; + +#endif // #ifndef liblldb_SymbolFileDWARFDebugMap_h_ diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp new file mode 100644 index 000000000000..d7da35675c7c --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp @@ -0,0 +1,401 @@ +//===-- SymbolFileSymtab.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileSymtab.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Function.h" + +using namespace lldb; +using namespace lldb_private; + +void +SymbolFileSymtab::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileSymtab::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +SymbolFileSymtab::GetPluginNameStatic() +{ + return "symbol-file.symtab"; +} + +const char * +SymbolFileSymtab::GetPluginDescriptionStatic() +{ + return "Reads debug symbols from an object file's symbol table."; +} + + +SymbolFile* +SymbolFileSymtab::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileSymtab(obj_file); +} + +SymbolFileSymtab::SymbolFileSymtab(ObjectFile* obj_file) : + SymbolFile(obj_file), + m_source_indexes(), + m_func_indexes(), + m_code_indexes(), + m_data_indexes(), + m_addr_indexes() +{ +} + +SymbolFileSymtab::~SymbolFileSymtab() +{ +} + + +uint32_t +SymbolFileSymtab::GetAbilities () +{ + uint32_t abilities = 0; + const Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) + { + + //---------------------------------------------------------------------- + // The snippet of code below will get the indexes the module symbol + // table entries that are code, data, or function related (debug info), + // sort them by value (address) and dump the sorted symbols. + //---------------------------------------------------------------------- + symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile, m_source_indexes); + if (!m_source_indexes.empty()) + { + abilities |= CompileUnits; + } + symtab->AppendSymbolIndexesWithType(eSymbolTypeFunction, m_func_indexes); + if (!m_func_indexes.empty()) + { + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + abilities |= Functions; + } + + symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, m_code_indexes); + if (!m_code_indexes.empty()) + { + symtab->SortSymbolIndexesByValue(m_code_indexes, true); + abilities |= Labels; + } + + symtab->AppendSymbolIndexesWithType(eSymbolTypeData, m_data_indexes); + + if (!m_data_indexes.empty()) + { + symtab->SortSymbolIndexesByValue(m_data_indexes, true); + abilities |= GlobalVariables; + } + } + + return abilities; +} + +uint32_t +SymbolFileSymtab::GetNumCompileUnits() +{ + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + return 1; + + // If we have any source file symbols we will logically orgnize the object symbols + // using these. + return m_source_indexes.size(); +} + +CompUnitSP +SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx) +{ + CompUnitSP cu_sp; + + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + { + const FileSpec &obj_file_spec = m_obj_file->GetFileSpec(); + if (obj_file_spec) + cu_sp.reset(new CompileUnit(m_obj_file->GetModule(), NULL, obj_file_spec, 0, Language::Unknown)); + + } + else if (idx < m_source_indexes.size()) + { + const Symbol *cu_symbol = m_obj_file->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]); + if (cu_symbol) + cu_sp.reset(new CompileUnit(m_obj_file->GetModule(), NULL, cu_symbol->GetMangled().GetName().AsCString(), 0, Language::Unknown)); + } + return cu_sp; +} + +size_t +SymbolFileSymtab::ParseCompileUnitFunctions (const SymbolContext &sc) +{ + size_t num_added = 0; + // We must at least have a valid compile unit + assert (sc.comp_unit != NULL); + const Symtab *symtab = m_obj_file->GetSymtab(); + const Symbol *curr_symbol = NULL; + const Symbol *next_symbol = NULL; +// const char *prefix = m_obj_file->SymbolPrefix(); +// if (prefix == NULL) +// prefix == ""; +// +// const uint32_t prefix_len = strlen(prefix); + + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + { + // The only time we will have a user ID of zero is when we don't have + // and source file symbols and we declare one compile unit for the + // entire object file + if (!m_func_indexes.empty()) + { + + } + + if (!m_code_indexes.empty()) + { +// StreamFile s(stdout); +// symtab->Dump(&s, m_code_indexes); + + uint32_t idx = 0; // Index into the indexes + const uint32_t num_indexes = m_code_indexes.size(); + for (idx = 0; idx < num_indexes; ++idx) + { + uint32_t symbol_idx = m_code_indexes[idx]; + curr_symbol = symtab->SymbolAtIndex(symbol_idx); + if (curr_symbol) + { + // Union of all ranges in the function DIE (if the function is discontiguous) + AddressRange func_range(curr_symbol->GetValue(), 0); + if (func_range.GetBaseAddress().IsSectionOffset()) + { + uint32_t symbol_size = curr_symbol->GetByteSize(); + if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling()) + func_range.SetByteSize(symbol_size); + else if (idx + 1 < num_indexes) + { + next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]); + if (next_symbol) + { + func_range.SetByteSize(next_symbol->GetValue().GetOffset() - curr_symbol->GetValue().GetOffset()); + } + } + + FunctionSP func_sp(new Function(sc.comp_unit, + symbol_idx, // UserID is the DIE offset + LLDB_INVALID_UID, // We don't have any type info for this function + curr_symbol->GetMangled(), // Linker/mangled name + NULL, // no return type for a code symbol... + func_range)); // first address range + + if (func_sp.get() != NULL) + { + sc.comp_unit->AddFunction(func_sp); + ++num_added; + } + } + } + } + + } + } + else + { + // We assume we + } + return num_added; +} + +bool +SymbolFileSymtab::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + return false; +} + +bool +SymbolFileSymtab::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files) +{ + return false; +} + +size_t +SymbolFileSymtab::ParseFunctionBlocks (const SymbolContext &sc) +{ + return 0; +} + + +size_t +SymbolFileSymtab::ParseTypes (const SymbolContext &sc) +{ + return 0; +} + + +size_t +SymbolFileSymtab::ParseVariablesForContext (const SymbolContext& sc) +{ + return 0; +} + +Type* +SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid) +{ + return NULL; +} + + + +uint32_t +SymbolFileSymtab::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + if (m_obj_file->GetSymtab() == NULL) + return 0; + + uint32_t resolved_flags = 0; + if (resolve_scope & eSymbolContextSymbol) + { + sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + return resolved_flags; +} + +uint32_t +SymbolFileSymtab::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindGlobalVariables(const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileSymtab::FindFunctions (name = '%s')", + name.GetCString()); + + Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) + { + const uint32_t start_size = sc_list.GetSize(); + std::vector symbol_indexes; + symtab->FindAllSymbolsWithNameAndType (name, eSymbolTypeFunction, symbol_indexes); + symtab->FindAllSymbolsWithNameAndType (name, eSymbolTypeCode, symbol_indexes); + const uint32_t num_matches = symbol_indexes.size(); + if (num_matches) + { + SymbolContext sc(m_obj_file->GetModule()); + for (uint32_t i=0; iSymbolAtIndex(symbol_indexes[i]); + sc_list.Append(sc); + } + } + return sc_list.GetSize() - start_size; + } + return 0; +} + +uint32_t +SymbolFileSymtab::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileSymtab::FindFunctions (regex = '%s')", + regex.GetText()); + + return 0; +} + +//uint32_t +//SymbolFileSymtab::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// return 0; +//} +// +//uint32_t +//SymbolFileSymtab::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// return 0; +//} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolFileSymtab::GetPluginName() +{ + return "SymbolFileSymtab"; +} + +const char * +SymbolFileSymtab::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileSymtab::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileSymtab::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolFileSymtab::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolFileSymtab::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h new file mode 100644 index 000000000000..ac73f2945855 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h @@ -0,0 +1,136 @@ +//===-- SymbolFileSymtab.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolFileSymtab_h_ +#define liblldb_SymbolFileSymtab_h_ + +#include "lldb/Symbol/SymbolFile.h" +#include + +class SymbolFileSymtab : public lldb_private::SymbolFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile* + CreateInstance (lldb_private::ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileSymtab(lldb_private::ObjectFile* obj_file); + + virtual + ~SymbolFileSymtab(); + + virtual uint32_t GetAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t + GetNumCompileUnits(); + + virtual lldb::CompUnitSP + ParseCompileUnitAtIndex(uint32_t index); + + virtual size_t + ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + + virtual bool + ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + + virtual bool + ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList &support_files); + + virtual size_t + ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseTypes (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* + ResolveTypeUID(lldb::user_id_t type_uid); + + virtual uint32_t + ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + + virtual uint32_t + ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindGlobalVariables(const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + + virtual uint32_t + FindGlobalVariables(const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + + virtual uint32_t + FindFunctions(const lldb_private::ConstString &name, bool append, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindFunctions(const lldb_private::RegularExpression& regex, bool append, lldb_private::SymbolContextList& sc_list); + +// virtual uint32_t +// FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); + +// virtual uint32_t +// FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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: + std::vector m_source_indexes; + std::vector m_func_indexes; + std::vector m_code_indexes; + std::vector m_data_indexes; + std::vector m_addr_indexes; // Anything that needs to go into an search by address + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolFileSymtab); +}; + + +#endif // liblldb_SymbolFileSymtab_h_ diff --git a/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp new file mode 100644 index 000000000000..270d7ed0f5da --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp @@ -0,0 +1,339 @@ +//===-- SymbolVendorMacOSX.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolVendorMacOSX.h" + +#include // DebugSymbols needs this on Leopard... + +#include + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SymbolVendorMacOSX constructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::SymbolVendorMacOSX(Module *module) : + SymbolVendor(module) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::~SymbolVendorMacOSX() +{ +} + + +static bool +UUIDsMatch(Module *module, ObjectFile *ofile) +{ + if (module && ofile) + { + // Make sure the UUIDs match + UUID dsym_uuid; + if (ofile->GetUUID(&dsym_uuid)) + return dsym_uuid == module->GetUUID(); + } + return false; +} + + +//ObjectFile * +//LocateDSYMMachFileInDSYMBundle (Module* module, FileSpec& dsym_fspec) +//{ +// ObjectFile *dsym_objfile = NULL; +// +// char path[PATH_MAX]; +// +// if (dsym_fspec.GetPath(path, sizeof(path))) +// { +// size_t path_len = strlen(path); +// const char *bundle_subpath = "/Contents/Resources/DWARF/"; +// if (path_len > 0) +// { +// if (path[path_len-1] == '/') +// ::strncat (path, bundle_subpath + 1, sizeof(path)); +// else +// ::strncat (path, bundle_subpath, sizeof(path)); +// ::strncat (path, dsym_fspec.GetFilename().AsCString(), sizeof(path)); +// +// path_len = strlen(path); +// +// if (::strcasecmp (&path[path_len - strlen(".dSYM")], ".dSYM") == 0) +// { +// path[path_len - ::strlen(".dSYM")] = '\0'; +// dsym_fspec.SetFile(path); +// dsym_objfile = ObjectFile::FindPlugin(module, &dsym_fspec, 0); +// } +// } +// } +// return dsym_objfile; +//} +// +//CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url) __attribute__((weak_import)); + + +//ObjectFile * +//FindDSYMUsingDebugSymbols (Module* module, FileSpec& dsym_fspec) +//{ +// Timer scoped_locate("FindDSYMUsingDebugSymbols"); +// dsym_fspec.Clear(); +// ObjectFile *dsym_objfile = NULL; +// if (module->GetUUID().IsValid()) +// { +// // Try and locate the dSYM file using DebugSymbols first +// const UInt8 *module_uuid = (const UInt8 *)module->GetUUID().GetBytes(); +// if (module_uuid != NULL) +// { +// CFUUIDRef module_uuid_ref; +// module_uuid_ref = ::CFUUIDCreateWithBytes ( NULL, +// module_uuid[0], +// module_uuid[1], +// module_uuid[2], +// module_uuid[3], +// module_uuid[4], +// module_uuid[5], +// module_uuid[6], +// module_uuid[7], +// module_uuid[8], +// module_uuid[9], +// module_uuid[10], +// module_uuid[11], +// module_uuid[12], +// module_uuid[13], +// module_uuid[14], +// module_uuid[15]); +// +// if (module_uuid_ref) +// { +// CFURLRef dsym_url = NULL; +// CFURLRef exec_url = NULL; +// +// // if (DBGCopyFullDSYMURLForUUID) +// { +// char exec_path[PATH_MAX]; +// if (module->GetFileSpec().GetPath(exec_path, sizeof(exec_path))) +// { +// exec_url = CFURLCreateFromFileSystemRepresentation ( NULL, +// (const UInt8 *)exec_path, +// strlen(exec_path), +// FALSE); +// } +// +// dsym_url = DBGCopyFullDSYMURLForUUID(module_uuid_ref, exec_url); +// } +// // else +// // { +// // dsym_url = DBGCopyDSYMURLForUUID(module_uuid_ref); +// // } +// +// if (exec_url) +// { +// ::CFRelease (exec_url); +// exec_url = NULL; +// } +// +// ::CFRelease(module_uuid_ref); +// module_uuid_ref = NULL; +// +// if (dsym_url) +// { +// char dsym_path[PATH_MAX]; +// Boolean success = CFURLGetFileSystemRepresentation (dsym_url, true, (UInt8*)dsym_path, sizeof(dsym_path)-1); +// +// ::CFRelease(dsym_url), dsym_url = NULL; +// +// if (success) +// { +// dsym_fspec.SetFile(dsym_path); +// +// // Some newer versions of DebugSymbols will return a full path into a dSYM bundle +// // that points to the correct mach file within the dSYM bundle (MH_DSYM mach file +// // type). +// dsym_objfile = ObjectFile::FindPlugin(module, &dsym_fspec, 0); +// +// // Olders versions of DebugSymbols will return a path to a dSYM bundle. +// if (dsym_objfile == NULL) +// dsym_objfile = LocateDSYMMachFileInDSYMBundle (module, dsym_fspec); +// } +// } +// } +// } +// } +// return dsym_objfile; +//} + +static void +ReplaceDSYMSectionsWithExecutableSections (ObjectFile *exec_objfile, ObjectFile *dsym_objfile) +{ + // We need both the executable and the dSYM to live off of the + // same section lists. So we take all of the sections from the + // executable, and replace them in the dSYM. This allows section + // offset addresses that come from the dSYM to automatically + // get updated as images (shared libraries) get loaded and + // unloaded. + SectionList *exec_section_list = exec_objfile->GetSectionList(); + SectionList *dsym_section_list = dsym_objfile->GetSectionList(); + if (exec_section_list && dsym_section_list) + { + const uint32_t num_exec_sections = dsym_section_list->GetSize(); + uint32_t exec_sect_idx; + for (exec_sect_idx = 0; exec_sect_idx < num_exec_sections; ++exec_sect_idx) + { + SectionSP exec_sect_sp(exec_section_list->GetSectionAtIndex(exec_sect_idx)); + if (exec_sect_sp.get()) + { + // Try and replace any sections that exist in both the executable + // and in the dSYM with those from the executable. If we fail to + // replace the one in the dSYM, then add the executable section to + // the dSYM. + if (dsym_section_list->ReplaceSection(exec_sect_sp->GetID(), exec_sect_sp, 0) == false) + dsym_section_list->AddSection(exec_sect_sp); + } + } + } +} + +void +SymbolVendorMacOSX::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolVendorMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +SymbolVendorMacOSX::GetPluginNameStatic() +{ + return "symbol-vendor.macosx"; +} + +const char * +SymbolVendorMacOSX::GetPluginDescriptionStatic() +{ + return "Symbol vendor for MacOSX that looks for dSYM files that match executables."; +} + + + +//---------------------------------------------------------------------- +// CreateInstance +// +// Platforms can register a callback to use when creating symbol +// vendors to allow for complex debug information file setups, and to +// also allow for finding separate debug information files. +//---------------------------------------------------------------------- +SymbolVendor* +SymbolVendorMacOSX::CreateInstance(Module* module) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolVendorMacOSX::CreateInstance (module = %s/%s)", + module->GetFileSpec().GetDirectory().AsCString(), + module->GetFileSpec().GetFilename().AsCString()); + SymbolVendorMacOSX* symbol_vendor = new SymbolVendorMacOSX(module); + if (symbol_vendor) + { + char path[PATH_MAX]; + path[0] = '\0'; + + // Try and locate the dSYM file on Mac OS X + ObjectFile * obj_file = module->GetObjectFile(); + if (obj_file) + { + Timer scoped_timer2 ("SymbolVendorMacOSX::CreateInstance () locate dSYM", + "SymbolVendorMacOSX::CreateInstance (module = %s/%s) locate dSYM", + module->GetFileSpec().GetDirectory().AsCString(), + module->GetFileSpec().GetFilename().AsCString()); + + FileSpec dsym_fspec; + std::auto_ptr dsym_objfile_ap; + const FileSpec &file_spec = obj_file->GetFileSpec(); + if (file_spec) + { + dsym_fspec = Symbols::LocateExecutableSymbolFile (&file_spec, &module->GetArchitecture(), &module->GetUUID()); + + if (dsym_fspec) + { + dsym_objfile_ap.reset(ObjectFile::FindPlugin(module, &dsym_fspec, 0, dsym_fspec.GetByteSize())); + if (UUIDsMatch(module, dsym_objfile_ap.get())) + { + ReplaceDSYMSectionsWithExecutableSections (obj_file, dsym_objfile_ap.get()); + symbol_vendor->AddSymbolFileRepresendation(dsym_objfile_ap.release()); + return symbol_vendor; + } + } + } + + // Just create our symbol vendor using the current objfile as this is either + // an executable with no dSYM (that we could locate), and executable with + // a dSYM that has a UUID that doesn't match, or it is a dSYM file itself. + symbol_vendor->AddSymbolFileRepresendation(obj_file); + } + } + return symbol_vendor; +} + + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolVendorMacOSX::GetPluginName() +{ + return "SymbolVendorMacOSX"; +} + +const char * +SymbolVendorMacOSX::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolVendorMacOSX::GetPluginVersion() +{ + return 1; +} + +void +SymbolVendorMacOSX::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolVendorMacOSX::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolVendorMacOSX::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h new file mode 100644 index 000000000000..adbf6489e27b --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h @@ -0,0 +1,71 @@ +//===-- SymbolVendorMacOSX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolVendorMacOSX_h_ +#define liblldb_SymbolVendorMacOSX_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolVendor.h" + +class SymbolVendorMacOSX : public lldb_private::SymbolVendor +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolVendor* + CreateInstance (lldb_private::Module *module); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendorMacOSX (lldb_private::Module *module); + + virtual + ~SymbolVendorMacOSX(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + 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); + + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolVendorMacOSX); +}; + +#endif // liblldb_SymbolVendorMacOSX_h_ diff --git a/lldb/source/Symbol/Block.cpp b/lldb/source/Symbol/Block.cpp new file mode 100644 index 000000000000..321411940f41 --- /dev/null +++ b/lldb/source/Symbol/Block.cpp @@ -0,0 +1,641 @@ +//===-- Block.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/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +using namespace lldb; +using namespace lldb_private; + +Block::Block(user_id_t uid, uint32_t depth, BlockList* blocks) : + UserID(uid), + m_block_list(blocks), + m_depth(depth), + m_ranges(), + m_inlineInfoSP(), + m_variables() +{ +} + +Block::Block(const Block& rhs) : + UserID(rhs), + m_block_list(rhs.m_block_list), + m_depth(rhs.m_depth), + m_ranges(rhs.m_ranges), + m_inlineInfoSP(rhs.m_inlineInfoSP), + m_variables(rhs.m_variables) +{ +} + +const Block& +Block::operator= (const Block& rhs) +{ + if (this != &rhs) + { + UserID::operator= (rhs); + m_block_list = rhs.m_block_list; + m_depth = rhs.m_depth; + m_ranges = rhs.m_ranges; + m_inlineInfoSP = rhs.m_inlineInfoSP; + m_variables = rhs.m_variables; + } + return *this; +} + +Block::~Block () +{ +} + +void +Block::Dump(Stream *s, addr_t base_addr, int32_t depth, bool show_context) const +{ + if (depth < 0) + { + // We have a depth that is less than zero, print our parent blocks + // first + m_block_list->Dump(s, GetParentUID(), depth + 1, show_context); + } + + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + *s << "Block" << ((const UserID&)*this); + user_id_t parentID = GetParentUID(); + const Block* parent_block = NULL; + if (parentID != Block::InvalidID) + { + parent_block = m_block_list->GetBlockByID(parentID); + s->Printf(", parent = {0x%8.8x}", parentID); + } + if (m_inlineInfoSP.get() != NULL) + m_inlineInfoSP->Dump(s); + + if (!m_ranges.empty()) + { + *s << ", ranges ="; + std::vector::const_iterator pos; + std::vector::const_iterator end = m_ranges.end(); + for (pos = m_ranges.begin(); pos != end; ++pos) + { + if (parent_block != NULL && parent_block->Contains(*pos) == false) + *s << '!'; + else + *s << ' '; + pos->Dump(s, base_addr); + } + } + s->EOL(); + + if (depth > 0) + { + s->IndentMore(); + + if (m_variables.get()) + { + m_variables->Dump(s, show_context); + } + + uint32_t blockID = m_block_list->GetFirstChild(GetID()); + while (blockID != Block::InvalidID) + { + m_block_list->Dump(s, blockID, depth - 1, show_context); + + blockID = m_block_list->GetSibling(blockID); + } + + s->IndentLess(); + } + +} + + +void +Block::CalculateSymbolContext(SymbolContext* sc) +{ + sc->block = this; + m_block_list->GetFunction()->CalculateSymbolContext(sc); +} + +void +Block::DumpStopContext (Stream *s, const SymbolContext *sc) +{ + user_id_t parentID = GetParentUID(); + Block* parent_block = NULL; + if (parentID != Block::InvalidID) + parent_block = m_block_list->GetBlockByID(parentID); + + InlineFunctionInfo* inline_info = InlinedFunctionInfo (); + if (inline_info) + { + const Declaration &call_site = inline_info->GetCallSite(); + if (sc) + { + // First frame, dump the first inline call site +// if (call_site.IsValid()) +// { +// s->PutCString(" at "); +// call_site.DumpStopContext (s); +// } + s->PutCString (" [inlined]"); + } + s->EOL(); + inline_info->DumpStopContext (s); + if (sc == NULL) + { + if (call_site.IsValid()) + { + s->PutCString(" at "); + call_site.DumpStopContext (s); + } + } + } + + if (sc) + { + // If we have any inlined functions, this will be the deepest most + // inlined location + if (sc->line_entry.IsValid()) + { + s->PutCString(" at "); + sc->line_entry.DumpStopContext (s); + } + } + if (parent_block) + parent_block->Block::DumpStopContext (s, NULL); +} + + +void +Block::DumpSymbolContext(Stream *s) +{ + m_block_list->GetFunction()->DumpSymbolContext(s); + s->Printf(", Block{0x%8.8x}", GetID()); +} + +bool +Block::Contains (addr_t range_offset) const +{ + return VMRange::ContainsValue(m_ranges, range_offset); +} + +bool +Block::Contains (const VMRange& range) const +{ + return VMRange::ContainsRange(m_ranges, range); +} + + + +bool +BlockList::BlockContainsBlockWithID (const user_id_t block_id, const user_id_t find_block_id) const +{ + if (block_id == Block::InvalidID) + return false; + + if (block_id == find_block_id) + return true; + else + { + user_id_t child_block_id = GetFirstChild(block_id); + while (child_block_id != Block::InvalidID) + { + if (BlockContainsBlockWithID (child_block_id, find_block_id)) + return true; + child_block_id = GetSibling(child_block_id); + } + } + + return false; +} + +bool +Block::ContainsBlockWithID (user_id_t block_id) const +{ + return m_block_list->BlockContainsBlockWithID (GetID(), block_id); +} + + +void +Block::AddRange(addr_t start_offset, addr_t end_offset) +{ + m_ranges.resize(m_ranges.size()+1); + m_ranges.back().Reset(start_offset, end_offset); +} + +InlineFunctionInfo* +Block::InlinedFunctionInfo () +{ + return m_inlineInfoSP.get(); +} + +const InlineFunctionInfo* +Block::InlinedFunctionInfo () const +{ + return m_inlineInfoSP.get(); +} + +// Return the current number of bytes that this object occupies in memory +size_t +Block::MemorySize() const +{ + size_t mem_size = sizeof(Block) + m_ranges.size() * sizeof(VMRange); + if (m_inlineInfoSP.get()) + mem_size += m_inlineInfoSP->MemorySize(); + if (m_variables.get()) + mem_size += m_variables->MemorySize(); + return mem_size; + +} + +user_id_t +Block::GetParentUID() const +{ + return m_block_list->GetParent(GetID()); +} + +user_id_t +Block::GetSiblingUID() const +{ + return m_block_list->GetSibling(GetID()); +} + +user_id_t +Block::GetFirstChildUID() const +{ + return m_block_list->GetFirstChild(GetID()); +} + +user_id_t +Block::AddChild(user_id_t userID) +{ + return m_block_list->AddChild(GetID(), userID); +} + +void +Block::SetInlinedFunctionInfo(const char *name, const char *mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr) +{ + m_inlineInfoSP.reset(new InlineFunctionInfo(name, mangled, decl_ptr, call_decl_ptr)); +} + +BlockList::BlockList(Function *function, const AddressRange& range) : + m_function(function), + m_range(range), + m_blocks() +{ +} + +BlockList::~BlockList() +{ +} + +AddressRange & +BlockList::GetAddressRange() +{ + return m_range; +} + +const AddressRange & +BlockList::GetAddressRange() const +{ + return m_range; +} + +void +BlockList::Dump(Stream *s, user_id_t blockID, uint32_t depth, bool show_context) const +{ + const Block* block = GetBlockByID(blockID); + if (block) + block->Dump(s, m_range.GetBaseAddress().GetFileAddress(), depth, show_context); +} + +Function * +BlockList::GetFunction() +{ + return m_function; +} + + +const Function * +BlockList::GetFunction() const +{ + return m_function; +} + +user_id_t +BlockList::GetParent(user_id_t blockID) const +{ + collection::const_iterator end = m_blocks.end(); + collection::const_iterator begin = m_blocks.begin(); + collection::const_iterator pos = std::find_if(begin, end, UserID::IDMatches(blockID)); + + if (pos != end && pos != begin && pos->Depth() > 0) + { + const uint32_t parent_depth = pos->Depth() - 1; + + while (--pos >= begin) + { + if (pos->Depth() == parent_depth) + return pos->GetID(); + } + } + return Block::InvalidID; +} + +user_id_t +BlockList::GetSibling(user_id_t blockID) const +{ + collection::const_iterator end = m_blocks.end(); + collection::const_iterator pos = std::find_if(m_blocks.begin(), end, UserID::IDMatches(blockID)); + + if (pos != end) + { + const uint32_t sibling_depth = pos->Depth(); + while (++pos != end) + { + uint32_t depth = pos->Depth(); + if (depth == sibling_depth) + return pos->GetID(); + if (depth < sibling_depth) + break; + } + } + return Block::InvalidID; +} + +user_id_t +BlockList::GetFirstChild(user_id_t blockID) const +{ + if (!m_blocks.empty()) + { + if (blockID == Block::RootID) + { + return m_blocks.front().GetID(); + } + else + { + collection::const_iterator end = m_blocks.end(); + collection::const_iterator pos = std::find_if(m_blocks.begin(), end, UserID::IDMatches(blockID)); + + if (pos != end) + { + collection::const_iterator child_pos = pos + 1; + if (child_pos != end) + { + if (child_pos->Depth() == pos->Depth() + 1) + return child_pos->GetID(); + } + } + } + } + return Block::InvalidID; +} + + +// Return the current number of bytes that this object occupies in memory +size_t +BlockList::MemorySize() const +{ + size_t mem_size = sizeof(BlockList); + + collection::const_iterator pos, end = m_blocks.end(); + for (pos = m_blocks.begin(); pos != end; ++pos) + mem_size += pos->MemorySize(); // Each block can vary in size + + return mem_size; + +} + +user_id_t +BlockList::AddChild (user_id_t parentID, user_id_t childID) +{ + bool added = false; + if (parentID == Block::RootID) + { + assert(m_blocks.empty()); + Block block(childID, 0, this); + m_blocks.push_back(block); + added = true; + } + else + { + collection::iterator end = m_blocks.end(); + collection::iterator parent_pos = std::find_if(m_blocks.begin(), end, UserID::IDMatches(parentID)); + assert(parent_pos != end); + if (parent_pos != end) + { + const uint32_t parent_sibling_depth = parent_pos->Depth(); + + collection::iterator insert_pos = parent_pos; + collection::iterator prev_sibling = end; + while (++insert_pos != end) + { + if (insert_pos->Depth() <= parent_sibling_depth) + break; + } + + Block child_block(childID, parent_pos->Depth() + 1, this); + collection::iterator child_pos = m_blocks.insert(insert_pos, child_block); + added = true; + } + } + if (added) + return childID; + return Block::InvalidID; +} + +const Block * +BlockList::GetBlockByID(user_id_t blockID) const +{ + if (m_blocks.empty()) + return NULL; + + if (blockID == Block::RootID) + blockID = m_blocks.front().GetID(); + + collection::const_iterator end = m_blocks.end(); + collection::const_iterator pos = std::find_if(m_blocks.begin(), end, UserID::IDMatches(blockID)); + if (pos != end) + return &(*pos); + return NULL; +} + +Block * +BlockList::GetBlockByID(user_id_t blockID) +{ + if (m_blocks.empty()) + return NULL; + + if (blockID == Block::RootID) + blockID = m_blocks.front().GetID(); + + collection::iterator end = m_blocks.end(); + collection::iterator pos = std::find_if(m_blocks.begin(), end, UserID::IDMatches(blockID)); + if (pos != end) + return &(*pos); + return NULL; +} + +bool +BlockList::AddRange(user_id_t blockID, addr_t start_offset, addr_t end_offset) +{ + Block *block = GetBlockByID(blockID); + + if (block) + { + block->AddRange(start_offset, end_offset); + return true; + } + return false; +} +// +//const Block * +//BlockList::FindDeepestBlockForAddress (const Address &addr) +//{ +// if (m_range.Contains(addr)) +// { +// addr_t block_offset = addr.GetFileAddress() - m_range.GetBaseAddress().GetFileAddress(); +// collection::const_iterator pos, end = m_blocks.end(); +// collection::const_iterator deepest_match_pos = end; +// for (pos = m_blocks.begin(); pos != end; ++pos) +// { +// if (pos->Contains (block_offset)) +// { +// if (deepest_match_pos == end || deepest_match_pos->Depth() < pos->Depth()) +// deepest_match_pos = pos; +// } +// } +// if (deepest_match_pos != end) +// return &(*deepest_match_pos); +// } +// return NULL; +//} +// +bool +BlockList::SetInlinedFunctionInfo(user_id_t blockID, const char *name, const char *mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr) +{ + Block *block = GetBlockByID(blockID); + + if (block) + { + block->SetInlinedFunctionInfo(name, mangled, decl_ptr, call_decl_ptr); + return true; + } + return false; +} + +VariableListSP +BlockList::GetVariableList(user_id_t blockID, bool get_child_variables, bool can_create) +{ + VariableListSP variable_list_sp; + Block *block = GetBlockByID(blockID); + if (block) + variable_list_sp = block->GetVariableList(get_child_variables, can_create); + return variable_list_sp; +} + +bool +BlockList::IsEmpty() const +{ + return m_blocks.empty(); +} + + + +bool +BlockList::SetVariableList(user_id_t blockID, VariableListSP& variables) +{ + Block *block = GetBlockByID(blockID); + if (block) + { + block->SetVariableList(variables); + return true; + } + return false; + +} + + +VariableListSP +Block::GetVariableList (bool get_child_variables, bool can_create) +{ + VariableListSP variable_list_sp; + if (m_variables.get() == NULL && can_create) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + assert(sc.module_sp); + sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc); + } + + if (m_variables.get()) + { + variable_list_sp.reset(new VariableList()); + if (variable_list_sp.get()) + variable_list_sp->AddVariables(m_variables.get()); + + if (get_child_variables) + { + user_id_t block_id = GetFirstChildUID(); + while (block_id != Block::InvalidID) + { + Block *child_block = m_block_list->GetBlockByID(block_id); + assert(child_block); + VariableListSP child_block_variable_list(child_block->GetVariableList(get_child_variables, can_create)); + if (child_block_variable_list.get()) + variable_list_sp->AddVariables(child_block_variable_list.get()); + + block_id = child_block->GetSiblingUID(); + } + } + } + + return variable_list_sp; +} + +uint32_t +Block::AppendVariables (bool can_create, bool get_parent_variables, VariableList *variable_list) +{ + uint32_t num_variables_added = 0; + VariableListSP variable_list_sp(GetVariableList(false, can_create)); + + if (variable_list_sp.get()) + { + num_variables_added = variable_list_sp->GetSize(); + variable_list->AddVariables(variable_list_sp.get()); + } + + if (get_parent_variables) + { + user_id_t parentID = GetParentUID(); + if (parentID != Block::InvalidID) + { + Block* parent_block = m_block_list->GetBlockByID(parentID); + if (parent_block) + num_variables_added += parent_block->AppendVariables (can_create, get_parent_variables, variable_list); + } + } + return num_variables_added; +} + + +void +Block::SetVariableList(VariableListSP& variables) +{ + m_variables = variables; +} + +uint32_t +Block::Depth () const +{ + return m_depth; +} + diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp new file mode 100644 index 000000000000..be63de20c4c1 --- /dev/null +++ b/lldb/source/Symbol/ClangASTContext.cpp @@ -0,0 +1,2552 @@ +//===-- ClangASTContext.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangASTContext.h" + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/LangStandard.h" + +// Project includes +#include "lldb/Core/dwarf.h" + +using namespace lldb_private; +using namespace llvm; +using namespace clang; + + +static void +ParseLangArgs +( + LangOptions &Opts, + FrontendOptions::InputKind IK +) +{ + // FIXME: Cleanup per-file based stuff. + + // Set some properties which depend soley on the input kind; it would be nice + // to move these to the language standard, and have the driver resolve the + // input kind + language standard. + if (IK == FrontendOptions::IK_Asm) { + Opts.AsmPreprocessor = 1; + } else if (IK == FrontendOptions::IK_ObjC || + IK == FrontendOptions::IK_ObjCXX || + IK == FrontendOptions::IK_PreprocessedObjC || + IK == FrontendOptions::IK_PreprocessedObjCXX) { + Opts.ObjC1 = Opts.ObjC2 = 1; + } + + LangStandard::Kind LangStd = LangStandard::lang_unspecified; + + if (LangStd == LangStandard::lang_unspecified) { + // Based on the base language, pick one. + switch (IK) { + case FrontendOptions::IK_None: + case FrontendOptions::IK_AST: + assert(0 && "Invalid input kind!"); + case FrontendOptions::IK_OpenCL: + LangStd = LangStandard::lang_opencl; + break; + case FrontendOptions::IK_Asm: + case FrontendOptions::IK_C: + case FrontendOptions::IK_PreprocessedC: + case FrontendOptions::IK_ObjC: + case FrontendOptions::IK_PreprocessedObjC: + LangStd = LangStandard::lang_gnu99; + break; + case FrontendOptions::IK_CXX: + case FrontendOptions::IK_PreprocessedCXX: + case FrontendOptions::IK_ObjCXX: + case FrontendOptions::IK_PreprocessedObjCXX: + LangStd = LangStandard::lang_gnucxx98; + break; + } + } + + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Opts.BCPLComment = Std.hasBCPLComments(); + Opts.C99 = Std.isC99(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus0x = Std.isCPlusPlus0x(); + Opts.Digraphs = Std.hasDigraphs(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUInline = !Std.isC99(); + Opts.HexFloats = Std.hasHexFloats(); + Opts.ImplicitInt = Std.hasImplicitInt(); + + // OpenCL has some additional defaults. + if (LangStd == LangStandard::lang_opencl) { + Opts.OpenCL = 1; + Opts.AltiVec = 1; + Opts.CXXOperatorNames = 1; + Opts.LaxVectorConversions = 1; + } + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + +// if (Opts.CPlusPlus) +// Opts.CXXOperatorNames = !Args.hasArg(OPT_fno_operator_names); +// +// if (Args.hasArg(OPT_fobjc_gc_only)) +// Opts.setGCMode(LangOptions::GCOnly); +// else if (Args.hasArg(OPT_fobjc_gc)) +// Opts.setGCMode(LangOptions::HybridGC); +// +// if (Args.hasArg(OPT_print_ivar_layout)) +// Opts.ObjCGCBitmapPrint = 1; +// +// if (Args.hasArg(OPT_faltivec)) +// Opts.AltiVec = 1; +// +// if (Args.hasArg(OPT_pthread)) +// Opts.POSIXThreads = 1; +// +// llvm::StringRef Vis = getLastArgValue(Args, OPT_fvisibility, +// "default"); +// if (Vis == "default") + Opts.setVisibilityMode(LangOptions::Default); +// else if (Vis == "hidden") +// Opts.setVisibilityMode(LangOptions::Hidden); +// else if (Vis == "protected") +// Opts.setVisibilityMode(LangOptions::Protected); +// else +// Diags.Report(diag::err_drv_invalid_value) +// << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis; + +// Opts.OverflowChecking = Args.hasArg(OPT_ftrapv); + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + Opts.Trigraphs = !Opts.GNUMode; +// if (Args.hasArg(OPT_trigraphs)) +// Opts.Trigraphs = 1; +// +// Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, +// OPT_fno_dollars_in_identifiers, +// !Opts.AsmPreprocessor); +// Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); +// Opts.Microsoft = Args.hasArg(OPT_fms_extensions); +// Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); +// if (Args.hasArg(OPT_fno_lax_vector_conversions)) +// Opts.LaxVectorConversions = 0; +// Opts.Exceptions = Args.hasArg(OPT_fexceptions); +// Opts.RTTI = !Args.hasArg(OPT_fno_rtti); +// Opts.Blocks = Args.hasArg(OPT_fblocks); +// Opts.CharIsSigned = !Args.hasArg(OPT_fno_signed_char); +// Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); +// Opts.Freestanding = Args.hasArg(OPT_ffreestanding); +// Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; +// Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); +// Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); +// Opts.AccessControl = Args.hasArg(OPT_faccess_control); +// Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); +// Opts.MathErrno = !Args.hasArg(OPT_fno_math_errno); +// Opts.InstantiationDepth = getLastArgIntValue(Args, OPT_ftemplate_depth, 99, +// Diags); +// Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime); +// Opts.ObjCConstantStringClass = getLastArgValue(Args, +// OPT_fconstant_string_class); +// Opts.ObjCNonFragileABI = Args.hasArg(OPT_fobjc_nonfragile_abi); +// Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior); +// Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); +// Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); +// Opts.Static = Args.hasArg(OPT_static_define); + Opts.OptimizeSize = 0; + + // FIXME: Eliminate this dependency. +// unsigned Opt = +// Args.hasArg(OPT_Os) ? 2 : getLastArgIntValue(Args, OPT_O, 0, Diags); +// Opts.Optimize = Opt != 0; + unsigned Opt = 0; + + // This is the __NO_INLINE__ define, which just depends on things like the + // optimization level and -fno-inline, not actually whether the backend has + // inlining enabled. + // + // FIXME: This is affected by other options (-fno-inline). + Opts.NoInline = !Opt; + +// unsigned SSP = getLastArgIntValue(Args, OPT_stack_protector, 0, Diags); +// switch (SSP) { +// default: +// Diags.Report(diag::err_drv_invalid_value) +// << Args.getLastArg(OPT_stack_protector)->getAsString(Args) << SSP; +// break; +// case 0: Opts.setStackProtectorMode(LangOptions::SSPOff); break; +// case 1: Opts.setStackProtectorMode(LangOptions::SSPOn); break; +// case 2: Opts.setStackProtectorMode(LangOptions::SSPReq); break; +// } +} + +//---------------------------------------------------------------------- +// ClangASTContext constructor +//---------------------------------------------------------------------- +//ClangASTContext::ClangASTContext(Module *module) : +// m_target_triple(), +// m_ast_context_ap(), +// m_language_options_ap(), +// m_source_manager_ap(), +// m_target_info_ap(), +// m_identifier_table_ap(), +// m_selector_table_ap(), +// m_builtins_ap() +//{ +// if (module) +// { +// ObjectFile * objfile = module->GetObjectFile(); +// if (objfile) +// objfile->GetTargetTriple(m_target_triple); +// } +//} + +//ClangASTContext::ClangASTContext(const ConstString& target_triple) : +// m_target_triple(target_triple), +// m_ast_context_ap(), +// m_language_options_ap(), +// m_source_manager_ap(), +// m_target_info_ap(), +// m_identifier_table_ap(), +// m_selector_table_ap(), +// m_builtins_ap() +//{ +//} +ClangASTContext::ClangASTContext(const char *target_triple) : + m_target_triple(), + m_ast_context_ap(), + m_language_options_ap(), + m_source_manager_ap(), + m_diagnostic_ap(), + m_target_options_ap(), + m_target_info_ap(), + m_identifier_table_ap(), + m_selector_table_ap(), + m_builtins_ap() +{ + if (target_triple && target_triple[0]) + m_target_triple.assign (target_triple); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangASTContext::~ClangASTContext() +{ + m_builtins_ap.reset(); + m_selector_table_ap.reset(); + m_identifier_table_ap.reset(); + m_target_info_ap.reset(); + m_target_options_ap.reset(); + m_diagnostic_ap.reset(); + m_source_manager_ap.reset(); + m_language_options_ap.reset(); + m_ast_context_ap.reset(); +} + + +void +ClangASTContext::Clear() +{ + m_ast_context_ap.reset(); + m_language_options_ap.reset(); + m_source_manager_ap.reset(); + m_diagnostic_ap.reset(); + m_target_options_ap.reset(); + m_target_info_ap.reset(); + m_identifier_table_ap.reset(); + m_selector_table_ap.reset(); + m_builtins_ap.reset(); +} + +const char * +ClangASTContext::GetTargetTriple () +{ + return m_target_triple.c_str(); +} + +void +ClangASTContext::SetTargetTriple (const char *target_triple) +{ + Clear(); + m_target_triple.assign(target_triple); +} + + +ASTContext * +ClangASTContext::getASTContext() +{ + if (m_ast_context_ap.get() == NULL) + { + m_ast_context_ap.reset( + new ASTContext( + *getLanguageOptions(), + *getSourceManager(), + *getTargetInfo(), + *getIdentifierTable(), + *getSelectorTable(), + *getBuiltinContext())); + } + return m_ast_context_ap.get(); +} + +Builtin::Context * +ClangASTContext::getBuiltinContext() +{ + if (m_builtins_ap.get() == NULL) + m_builtins_ap.reset (new Builtin::Context(*getTargetInfo())); + return m_builtins_ap.get(); +} + +IdentifierTable * +ClangASTContext::getIdentifierTable() +{ + if (m_identifier_table_ap.get() == NULL) + m_identifier_table_ap.reset(new IdentifierTable (*ClangASTContext::getLanguageOptions(), NULL)); + return m_identifier_table_ap.get(); +} + +LangOptions * +ClangASTContext::getLanguageOptions() +{ + if (m_language_options_ap.get() == NULL) + { + m_language_options_ap.reset(new LangOptions()); + ParseLangArgs(*m_language_options_ap, FrontendOptions::IK_ObjCXX); +// InitializeLangOptions(*m_language_options_ap, FrontendOptions::IK_ObjCXX); + } + return m_language_options_ap.get(); +} + +SelectorTable * +ClangASTContext::getSelectorTable() +{ + if (m_selector_table_ap.get() == NULL) + m_selector_table_ap.reset (new SelectorTable()); + return m_selector_table_ap.get(); +} + +SourceManager * +ClangASTContext::getSourceManager() +{ + if (m_source_manager_ap.get() == NULL) + m_source_manager_ap.reset(new SourceManager(*getDiagnostic())); + return m_source_manager_ap.get(); +} + +Diagnostic * +ClangASTContext::getDiagnostic() +{ + if (m_diagnostic_ap.get() == NULL) + m_diagnostic_ap.reset(new Diagnostic()); + return m_diagnostic_ap.get(); +} + +TargetOptions * +ClangASTContext::getTargetOptions() +{ + if (m_target_options_ap.get() == NULL && !m_target_triple.empty()) + { + m_target_options_ap.reset (new TargetOptions()); + if (m_target_options_ap.get()) + m_target_options_ap->Triple = m_target_triple; + } + return m_target_options_ap.get(); +} + + +TargetInfo * +ClangASTContext::getTargetInfo() +{ + // target_triple should be something like "x86_64-apple-darwin10" + if (m_target_info_ap.get() == NULL && !m_target_triple.empty()) + m_target_info_ap.reset (TargetInfo::CreateTargetInfo(*getDiagnostic(), *getTargetOptions())); + return m_target_info_ap.get(); +} + +#pragma mark Basic Types + +static inline bool +QualTypeMatchesBitSize(const uint64_t bit_size, ASTContext *ast_context, QualType qual_type) +{ + uint64_t qual_type_bit_size = ast_context->getTypeSize(qual_type); + if (qual_type_bit_size == bit_size) + return true; + return false; +} + +void * +ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (lldb::Encoding encoding, uint32_t bit_size) +{ + ASTContext *ast_context = getASTContext(); + + assert (ast_context != NULL); + + return GetBuiltinTypeForEncodingAndBitSize (ast_context, encoding, bit_size); +} + +void * +ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (clang::ASTContext *ast_context, lldb::Encoding encoding, uint32_t bit_size) +{ + if (!ast_context) + return NULL; + + switch (encoding) + { + case lldb::eEncodingInvalid: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->VoidPtrTy)) + return ast_context->VoidPtrTy.getAsOpaquePtr(); + break; + + case lldb::eEncodingUint: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedCharTy)) + return ast_context->UnsignedCharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedShortTy)) + return ast_context->UnsignedShortTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedIntTy)) + return ast_context->UnsignedIntTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedLongTy)) + return ast_context->UnsignedLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedLongLongTy)) + return ast_context->UnsignedLongLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedInt128Ty)) + return ast_context->UnsignedInt128Ty.getAsOpaquePtr(); + break; + + case lldb::eEncodingSint: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->CharTy)) + return ast_context->CharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->ShortTy)) + return ast_context->ShortTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->IntTy)) + return ast_context->IntTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongTy)) + return ast_context->LongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongLongTy)) + return ast_context->LongLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->Int128Ty)) + return ast_context->Int128Ty.getAsOpaquePtr(); + break; + + case lldb::eEncodingIEEE754: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->FloatTy)) + return ast_context->FloatTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->DoubleTy)) + return ast_context->DoubleTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongDoubleTy)) + return ast_context->LongDoubleTy.getAsOpaquePtr(); + break; + + case lldb::eEncodingVector: + default: + break; + } + + return NULL; +} + +void * +ClangASTContext::GetBuiltinTypeForDWARFEncodingAndBitSize (const char *type_name, uint32_t dw_ate, uint32_t bit_size) +{ + ASTContext *ast_context = getASTContext(); + + #define streq(a,b) strcmp(a,b) == 0 + assert (ast_context != NULL); + if (ast_context) + { + switch (dw_ate) + { + default: + break; + + case DW_ATE_address: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->VoidPtrTy)) + return ast_context->VoidPtrTy.getAsOpaquePtr(); + break; + + case DW_ATE_boolean: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->BoolTy)) + return ast_context->BoolTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedCharTy)) + return ast_context->UnsignedCharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedShortTy)) + return ast_context->UnsignedShortTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedIntTy)) + return ast_context->UnsignedIntTy.getAsOpaquePtr(); + break; + + case DW_ATE_complex_float: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->FloatComplexTy)) + return ast_context->FloatComplexTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->DoubleComplexTy)) + return ast_context->DoubleComplexTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongDoubleComplexTy)) + return ast_context->LongDoubleComplexTy.getAsOpaquePtr(); + break; + + case DW_ATE_float: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->FloatTy)) + return ast_context->FloatTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->DoubleTy)) + return ast_context->DoubleTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongDoubleTy)) + return ast_context->LongDoubleTy.getAsOpaquePtr(); + break; + + case DW_ATE_signed: + if (type_name) + { + if (streq(type_name, "int") || + streq(type_name, "signed int")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->IntTy)) + return ast_context->IntTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->Int128Ty)) + return ast_context->Int128Ty.getAsOpaquePtr(); + } + + if (streq(type_name, "long int") || + streq(type_name, "long long int") || + streq(type_name, "signed long long")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongTy)) + return ast_context->LongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongLongTy)) + return ast_context->LongLongTy.getAsOpaquePtr(); + } + + if (streq(type_name, "short") || + streq(type_name, "short int") || + streq(type_name, "signed short") || + streq(type_name, "short signed int")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->ShortTy)) + return ast_context->ShortTy.getAsOpaquePtr(); + } + + if (streq(type_name, "char") || + streq(type_name, "signed char")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->CharTy)) + return ast_context->CharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->SignedCharTy)) + return ast_context->SignedCharTy.getAsOpaquePtr(); + } + + if (streq(type_name, "wchar_t")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->WCharTy)) + return ast_context->WCharTy.getAsOpaquePtr(); + } + + } + // We weren't able to match up a type name, just search by size + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->CharTy)) + return ast_context->CharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->ShortTy)) + return ast_context->ShortTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->IntTy)) + return ast_context->IntTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongTy)) + return ast_context->LongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->LongLongTy)) + return ast_context->LongLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->Int128Ty)) + return ast_context->Int128Ty.getAsOpaquePtr(); + break; + + case DW_ATE_signed_char: + if (type_name) + { + if (streq(type_name, "signed char")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->SignedCharTy)) + return ast_context->SignedCharTy.getAsOpaquePtr(); + } + } + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->CharTy)) + return ast_context->CharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->SignedCharTy)) + return ast_context->SignedCharTy.getAsOpaquePtr(); + break; + + case DW_ATE_unsigned: + if (type_name) + { + if (streq(type_name, "unsigned int")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedIntTy)) + return ast_context->UnsignedIntTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedInt128Ty)) + return ast_context->UnsignedInt128Ty.getAsOpaquePtr(); + } + + if (streq(type_name, "unsigned int") || + streq(type_name, "long unsigned int") || + streq(type_name, "unsigned long long")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedLongTy)) + return ast_context->UnsignedLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedLongLongTy)) + return ast_context->UnsignedLongLongTy.getAsOpaquePtr(); + } + + if (streq(type_name, "unsigned short") || + streq(type_name, "short unsigned int")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedShortTy)) + return ast_context->UnsignedShortTy.getAsOpaquePtr(); + } + if (streq(type_name, "unsigned char")) + { + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedCharTy)) + return ast_context->UnsignedCharTy.getAsOpaquePtr(); + } + + } + // We weren't able to match up a type name, just search by size + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedCharTy)) + return ast_context->UnsignedCharTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedShortTy)) + return ast_context->UnsignedShortTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedIntTy)) + return ast_context->UnsignedIntTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedLongTy)) + return ast_context->UnsignedLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedLongLongTy)) + return ast_context->UnsignedLongLongTy.getAsOpaquePtr(); + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedInt128Ty)) + return ast_context->UnsignedInt128Ty.getAsOpaquePtr(); + break; + + case DW_ATE_unsigned_char: + if (QualTypeMatchesBitSize (bit_size, ast_context, ast_context->UnsignedCharTy)) + return ast_context->UnsignedCharTy.getAsOpaquePtr(); + break; + + case DW_ATE_imaginary_float: + break; + } + } + // This assert should fire for anything that we don't catch above so we know + // to fix any issues we run into. + assert (!"error: ClangASTContext::GetClangTypeForDWARFEncodingAndSize() contains an unhandled encoding. Fix this ASAP!"); + return NULL; +} + +void * +ClangASTContext::GetVoidBuiltInType() +{ + return getASTContext()->VoidTy.getAsOpaquePtr(); +} + +void * +ClangASTContext::GetCStringType (bool is_const) +{ + QualType char_type(getASTContext()->CharTy); + + if (is_const) + char_type.addConst(); + + return getASTContext()->getPointerType(char_type).getAsOpaquePtr(); +} + +void * +ClangASTContext::GetVoidPtrType (bool is_const) +{ + return GetVoidPtrType(getASTContext(), is_const); +} + +void * +ClangASTContext::GetVoidPtrType (clang::ASTContext *ast_context, bool is_const) +{ + QualType void_ptr_type(ast_context->VoidPtrTy); + + if (is_const) + void_ptr_type.addConst(); + + return void_ptr_type.getAsOpaquePtr(); +} + +void * +ClangASTContext::CopyType(clang::ASTContext *dest_context, + clang::ASTContext *source_context, + void * clang_type) +{ + Diagnostic diagnostics; + FileManager file_manager; + ASTImporter importer(diagnostics, + *dest_context, file_manager, + *source_context, file_manager); + QualType ret = importer.Import(QualType::getFromOpaquePtr(clang_type)); + return ret.getAsOpaquePtr(); +} + +#pragma mark CVR modifiers + +void * +ClangASTContext::AddConstModifier (void *clang_type) +{ + if (clang_type) + { + QualType result(QualType::getFromOpaquePtr(clang_type)); + result.addConst(); + return result.getAsOpaquePtr(); + } + return NULL; +} + +void * +ClangASTContext::AddRestrictModifier (void *clang_type) +{ + if (clang_type) + { + QualType result(QualType::getFromOpaquePtr(clang_type)); + result.getQualifiers().setRestrict (true); + return result.getAsOpaquePtr(); + } + return NULL; +} + +void * +ClangASTContext::AddVolatileModifier (void *clang_type) +{ + if (clang_type) + { + QualType result(QualType::getFromOpaquePtr(clang_type)); + result.getQualifiers().setVolatile (true); + return result.getAsOpaquePtr(); + } + return NULL; +} + +#pragma mark Structure, Unions, Classes + +void * +ClangASTContext::CreateRecordType (const char *name, int kind, DeclContext *decl_ctx) +{ + ASTContext *ast_context = getASTContext(); + assert (ast_context != NULL); + + if (decl_ctx == NULL) + decl_ctx = ast_context->getTranslationUnitDecl(); + + // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and + // we will need to update this code. I was told to currently always use + // the CXXRecordDecl class since we often don't know from debug information + // if something is struct or a class, so we default to always use the more + // complete definition just in case. + CXXRecordDecl *decl = CXXRecordDecl::Create(*ast_context, + (TagDecl::TagKind)kind, + decl_ctx, + SourceLocation(), + name && name[0] ? &ast_context->Idents.get(name) : NULL); + + return ast_context->getTagDeclType(decl).getAsOpaquePtr(); +} + +bool +ClangASTContext::AddFieldToRecordType (void * record_clang_type, const char *name, void * field_type, int access, uint32_t bitfield_bit_size) +{ + if (record_clang_type == NULL || field_type == NULL) + return false; + + ASTContext *ast_context = getASTContext(); + IdentifierTable *identifier_table = getIdentifierTable(); + + assert (ast_context != NULL); + assert (identifier_table != NULL); + + QualType record_qual_type(QualType::getFromOpaquePtr(record_clang_type)); + + Type *clang_type = record_qual_type.getTypePtr(); + if (clang_type) + { + const RecordType *record_type = dyn_cast(clang_type); + + if (record_type) + { + RecordDecl *record_decl = record_type->getDecl(); + + CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + if (cxx_record_decl) + cxx_record_decl->setEmpty (false); + + clang::Expr *bit_width = NULL; + if (bitfield_bit_size != 0) + { + APInt bitfield_bit_size_apint(ast_context->getTypeSize(ast_context->IntTy), bitfield_bit_size); + bit_width = new (*ast_context)IntegerLiteral (bitfield_bit_size_apint, ast_context->IntTy, SourceLocation()); + } + FieldDecl *field = FieldDecl::Create(*ast_context, + record_decl, + SourceLocation(), + name ? &identifier_table->get(name) : NULL, // Identifier + QualType::getFromOpaquePtr(field_type), // Field type + NULL, // DeclaratorInfo * + bit_width, // BitWidth + false); // Mutable + + field->setAccess((AccessSpecifier)access); + + if (field) + { + record_decl->addDecl(field); + return true; + } + } + } + return false; +} + +bool +ClangASTContext::FieldIsBitfield (FieldDecl* field, uint32_t& bitfield_bit_size) +{ + return FieldIsBitfield(getASTContext(), field, bitfield_bit_size); +} + +bool +ClangASTContext::FieldIsBitfield +( + ASTContext *ast_context, + FieldDecl* field, + uint32_t& bitfield_bit_size +) +{ + if (ast_context == NULL || field == NULL) + return false; + + if (field->isBitField()) + { + Expr* bit_width_expr = field->getBitWidth(); + if (bit_width_expr) + { + llvm::APSInt bit_width_apsint; + if (bit_width_expr->isIntegerConstantExpr(bit_width_apsint, *ast_context)) + { + bitfield_bit_size = bit_width_apsint.getLimitedValue(UINT32_MAX); + return true; + } + } + } + return false; +} + +bool +ClangASTContext::RecordHasFields (const RecordDecl *record_decl) +{ + if (record_decl == NULL) + return false; + + if (!record_decl->field_empty()) + return true; + + // No fields, lets check this is a CXX record and check the base classes + const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + if (cxx_record_decl) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = cast(base_class->getType()->getAs()->getDecl()); + if (RecordHasFields(base_class_decl)) + return true; + } + } + return false; +} + +void +ClangASTContext::SetDefaultAccessForRecordFields (void *clang_qual_type, int default_accessibility, int *assigned_accessibilities, size_t num_assigned_accessibilities) +{ + if (clang_qual_type) + { + QualType qual_type(QualType::getFromOpaquePtr(clang_qual_type)); + Type *clang_type = qual_type.getTypePtr(); + if (clang_type) + { + RecordType *record_type = dyn_cast(clang_type); + if (record_type) + { + RecordDecl *record_decl = record_type->getDecl(); + if (record_decl) + { + uint32_t field_idx; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(), field_idx = 0; + field != field_end; + ++field, ++field_idx) + { + // If no accessibility was assigned, assign the correct one + if (field_idx < num_assigned_accessibilities && assigned_accessibilities[field_idx] == clang::AS_none) + field->setAccess ((AccessSpecifier)default_accessibility); + } + } + } + } + } +} + +#pragma mark C++ Base Classes + +CXXBaseSpecifier * +ClangASTContext::CreateBaseClassSpecifier (void *base_class_type, int access, bool is_virtual, bool base_of_class) +{ + if (base_class_type) + return new CXXBaseSpecifier(SourceRange(), is_virtual, base_of_class, (AccessSpecifier)access, QualType::getFromOpaquePtr(base_class_type)); + return NULL; +} + +bool +ClangASTContext::SetBaseClassesForClassType (void *class_clang_type, CXXBaseSpecifier const * const *base_classes, unsigned num_base_classes) +{ + if (class_clang_type) + { + ASTContext *ast_context = getASTContext(); + IdentifierTable *identifier_table = getIdentifierTable(); + + assert (ast_context != NULL); + assert (identifier_table != NULL); + + Type *clang_type = QualType::getFromOpaquePtr(class_clang_type).getTypePtr(); + if (clang_type) + { + RecordType *record_type = dyn_cast(clang_type); + if (record_type) + { + CXXRecordDecl *cxx_record_decl = dyn_cast(record_type->getDecl()); + if (cxx_record_decl) + { + //cxx_record_decl->setEmpty (false); + cxx_record_decl->setBases(base_classes, num_base_classes); + return true; + } + } + } + } + return false; +} + + +#pragma mark Aggregate Types + +bool +ClangASTContext::IsAggregateType (void *clang_type) +{ + if (clang_type == NULL) + return false; + + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + + if (qual_type->isAggregateType ()) + return true; + + switch (qual_type->getTypeClass()) + { + case Type::IncompleteArray: + case Type::VariableArray: + case Type::ConstantArray: + case Type::ExtVector: + case Type::Vector: + case Type::Record: + return true; + + case Type::Typedef: + return ClangASTContext::IsAggregateType (cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr()); + + default: + break; + } + // The clang type does have a value + return false; +} + +uint32_t +ClangASTContext::GetNumChildren (void *clang_qual_type, bool omit_empty_base_classes) +{ + if (clang_qual_type == NULL) + return 0; + + uint32_t num_children = 0; + QualType qual_type(QualType::getFromOpaquePtr(clang_qual_type)); + switch (qual_type->getTypeClass()) + { + case Type::Record: + { + const RecordType *record_type = cast(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + if (cxx_record_decl) + { + if (omit_empty_base_classes) + { + // Check each base classes to see if it or any of its + // base classes contain any fields. This can help + // limit the noise in variable views by not having to + // show base classes that contain no members. + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = cast(base_class->getType()->getAs()->getDecl()); + + // Skip empty base classes + if (RecordHasFields(base_class_decl) == false) + continue; + + num_children++; + } + } + else + { + // Include all base classes + num_children += cxx_record_decl->getNumBases(); + } + + } + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) + ++num_children; + } + break; + + case Type::ConstantArray: + num_children = cast(qual_type.getTypePtr())->getSize().getLimitedValue(); + break; + + case Type::Pointer: + { + PointerType *pointer_type = cast(qual_type.getTypePtr()); + QualType pointee_type = pointer_type->getPointeeType(); + uint32_t num_pointee_children = ClangASTContext::GetNumChildren (pointee_type.getAsOpaquePtr(), omit_empty_base_classes); + // If this type points to a simple type, then it has 1 child + if (num_pointee_children == 0) + num_children = 1; + else + num_children = num_pointee_children; + } + break; + + case Type::Typedef: + num_children = ClangASTContext::GetNumChildren (cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr(), omit_empty_base_classes); + break; + + default: + break; + } + return num_children; +} + + +void * +ClangASTContext::GetChildClangTypeAtIndex +( + const char *parent_name, + void *parent_clang_type, + uint32_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + 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 +) +{ + if (parent_clang_type) + + return GetChildClangTypeAtIndex (getASTContext(), + parent_name, + parent_clang_type, + idx, + transparent_pointers, + omit_empty_base_classes, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + return NULL; +} + +void * +ClangASTContext::GetChildClangTypeAtIndex +( + ASTContext *ast_context, + const char *parent_name, + void *parent_clang_type, + uint32_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + 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 +) +{ + if (parent_clang_type == NULL) + return NULL; + + if (idx < ClangASTContext::GetNumChildren (parent_clang_type, omit_empty_base_classes)) + { + uint32_t bit_offset; + child_bitfield_bit_size = 0; + child_bitfield_bit_offset = 0; + QualType parent_qual_type(QualType::getFromOpaquePtr(parent_clang_type)); + switch (parent_qual_type->getTypeClass()) + { + case Type::Record: + { + const RecordType *record_type = cast(parent_qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const ASTRecordLayout &record_layout = ast_context->getASTRecordLayout(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + if (cxx_record_decl) + { + // We might have base classes to print out first + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = NULL; + + // Skip empty base classes + if (omit_empty_base_classes) + { + base_class_decl = cast(base_class->getType()->getAs()->getDecl()); + if (RecordHasFields(base_class_decl) == false) + continue; + } + + if (idx == child_idx) + { + if (base_class_decl == NULL) + base_class_decl = cast(base_class->getType()->getAs()->getDecl()); + + + if (base_class->isVirtual()) + bit_offset = record_layout.getVBaseClassOffset(base_class_decl); + else + bit_offset = record_layout.getBaseClassOffset(base_class_decl); + + // Base classes should be a multiple of 8 bits in size + assert (bit_offset % 8 == 0); + child_byte_offset = bit_offset/8; + std::string base_class_type_name(base_class->getType().getAsString()); + + child_name.assign(base_class_type_name.c_str()); + + uint64_t clang_type_info_bit_size = ast_context->getTypeSize(base_class->getType()); + + // Base classes biut sizes should be a multiple of 8 bits in size + assert (clang_type_info_bit_size % 8 == 0); + child_byte_size = clang_type_info_bit_size / 8; + return base_class->getType().getAsOpaquePtr(); + } + // We don't increment the child index in the for loop since we might + // be skipping empty base classes + ++child_idx; + } + } + const unsigned num_fields = record_layout.getFieldCount(); + + // Make sure index is in range... + uint32_t field_idx = 0; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) + { + if (idx == child_idx) + { + // Print the member type if requested + // Print the member name and equal sign + child_name.assign(field->getNameAsString().c_str()); + + // Figure out the type byte size (field_type_info.first) and + // alignment (field_type_info.second) from the AST context. + std::pair field_type_info = ast_context->getTypeInfo(field->getType()); + assert(field_idx < num_fields); + + child_byte_size = field_type_info.first / 8; + + // Figure out the field offset within the current struct/union/class type + bit_offset = record_layout.getFieldOffset (field_idx); + child_byte_offset = bit_offset / 8; + if (ClangASTContext::FieldIsBitfield (ast_context, *field, child_bitfield_bit_size)) + child_bitfield_bit_offset = bit_offset % 8; + + return field->getType().getAsOpaquePtr(); + } + } + } + break; + + case Type::ConstantArray: + { + const ConstantArrayType *array = cast(parent_qual_type.getTypePtr()); + const uint64_t element_count = array->getSize().getLimitedValue(); + + if (idx < element_count) + { + std::pair field_type_info = ast_context->getTypeInfo(array->getElementType()); + + char element_name[32]; + ::snprintf (element_name, sizeof (element_name), "%s[%u]", parent_name ? parent_name : "", idx); + + child_name.assign(element_name); + assert(field_type_info.first % 8 == 0); + child_byte_size = field_type_info.first / 8; + child_byte_offset = idx * child_byte_size; + return array->getElementType().getAsOpaquePtr(); + } + } + break; + + case Type::Pointer: + { + PointerType *pointer_type = cast(parent_qual_type.getTypePtr()); + QualType pointee_type = pointer_type->getPointeeType(); + + if (transparent_pointers && ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + { + return GetChildClangTypeAtIndex (ast_context, + parent_name, + pointer_type->getPointeeType().getAsOpaquePtr(), + idx, + transparent_pointers, + omit_empty_base_classes, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + } + else + { + if (parent_name) + { + child_name.assign(1, '*'); + child_name += parent_name; + } + + // We have a pointer to an simple type + if (idx == 0) + { + std::pair clang_type_info = ast_context->getTypeInfo(pointee_type); + assert(clang_type_info.first % 8 == 0); + child_byte_size = clang_type_info.first / 8; + child_byte_offset = 0; + return pointee_type.getAsOpaquePtr(); + } + } + } + break; + + case Type::Typedef: + return GetChildClangTypeAtIndex (ast_context, + parent_name, + cast(parent_qual_type)->LookThroughTypedefs().getAsOpaquePtr(), + idx, + transparent_pointers, + omit_empty_base_classes, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + break; + + default: + break; + } + } + return false; +} + +static inline bool +BaseSpecifierIsEmpty (const CXXBaseSpecifier *b) +{ + return ClangASTContext::RecordHasFields(cast(b->getType()->getAs()->getDecl())) == false; +} + +static uint32_t +GetNumBaseClasses (const CXXRecordDecl *cxx_record_decl, bool omit_empty_base_classes) +{ + uint32_t num_bases = 0; + if (cxx_record_decl) + { + if (omit_empty_base_classes) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + // Skip empty base classes + if (omit_empty_base_classes) + { + if (BaseSpecifierIsEmpty (base_class)) + continue; + } + ++num_bases; + } + } + else + num_bases = cxx_record_decl->getNumBases(); + } + return num_bases; +} + + +static uint32_t +GetIndexForRecordBase +( + const RecordDecl *record_decl, + const CXXBaseSpecifier *base_spec, + bool omit_empty_base_classes +) +{ + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + +// const char *super_name = record_decl->getNameAsCString(); +// const char *base_name = base_spec->getType()->getAs()->getDecl()->getNameAsCString(); +// printf ("GetIndexForRecordChild (%s, %s)\n", super_name, base_name); +// + if (cxx_record_decl) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + if (omit_empty_base_classes) + { + if (BaseSpecifierIsEmpty (base_class)) + continue; + } + +// printf ("GetIndexForRecordChild (%s, %s) base[%u] = %s\n", super_name, base_name, +// child_idx, +// base_class->getType()->getAs()->getDecl()->getNameAsCString()); +// +// + if (base_class == base_spec) + return child_idx; + ++child_idx; + } + } + + return UINT32_MAX; +} + + +static uint32_t +GetIndexForRecordChild +( + const RecordDecl *record_decl, + NamedDecl *canonical_decl, + bool omit_empty_base_classes +) +{ + uint32_t child_idx = GetNumBaseClasses (dyn_cast(record_decl), omit_empty_base_classes); + +// const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); +// +//// printf ("GetIndexForRecordChild (%s, %s)\n", record_decl->getNameAsCString(), canonical_decl->getNameAsCString()); +// if (cxx_record_decl) +// { +// CXXRecordDecl::base_class_const_iterator base_class, base_class_end; +// for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); +// base_class != base_class_end; +// ++base_class) +// { +// if (omit_empty_base_classes) +// { +// if (BaseSpecifierIsEmpty (base_class)) +// continue; +// } +// +//// printf ("GetIndexForRecordChild (%s, %s) base[%u] = %s\n", +//// record_decl->getNameAsCString(), +//// canonical_decl->getNameAsCString(), +//// child_idx, +//// base_class->getType()->getAs()->getDecl()->getNameAsCString()); +// +// +// CXXRecordDecl *curr_base_class_decl = cast(base_class->getType()->getAs()->getDecl()); +// if (curr_base_class_decl == canonical_decl) +// { +// return child_idx; +// } +// ++child_idx; +// } +// } +// +// const uint32_t num_bases = child_idx; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); + field != field_end; + ++field, ++child_idx) + { +// printf ("GetIndexForRecordChild (%s, %s) field[%u] = %s\n", +// record_decl->getNameAsCString(), +// canonical_decl->getNameAsCString(), +// child_idx - num_bases, +// field->getNameAsCString()); + + if (field->getCanonicalDecl() == canonical_decl) + return child_idx; + } + + return UINT32_MAX; +} + +// Look for a child member (doesn't include base classes, but it does include +// their members) in the type hierarchy. Returns an index path into "clang_type" +// on how to reach the appropriate member. +// +// class A +// { +// public: +// int m_a; +// int m_b; +// }; +// +// class B +// { +// }; +// +// class C : +// public B, +// public A +// { +// }; +// +// If we have a clang type that describes "class C", and we wanted to looked +// "m_b" in it: +// +// With omit_empty_base_classes == false we would get an integer array back with: +// { 1, 1 } +// The first index 1 is the child index for "class A" within class C +// The second index 1 is the child index for "m_b" within class A +// +// With omit_empty_base_classes == true we would get an integer array back with: +// { 0, 1 } +// The first index 0 is the child index for "class A" within class C (since class B doesn't have any members it doesn't count) +// The second index 1 is the child index for "m_b" within class A + +size_t +ClangASTContext::GetIndexOfChildMemberWithName +( + ASTContext *ast_context, + void *clang_type, + const char *name, + bool omit_empty_base_classes, + std::vector& child_indexes +) +{ + if (clang_type && name && name[0]) + { + QualType qual_type(QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case Type::Record: + { + const RecordType *record_type = cast(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + + assert(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + + // Try and find a field that matches NAME + RecordDecl::field_iterator field, field_end; + StringRef name_sref(name); + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); + field != field_end; + ++field, ++child_idx) + { + if (field->getName().equals (name_sref)) + { + // We have to add on the number of base classes to this index! + child_indexes.push_back (child_idx + GetNumBaseClasses (cxx_record_decl, omit_empty_base_classes)); + return child_indexes.size(); + } + } + + if (cxx_record_decl) + { + const RecordDecl *parent_record_decl = cxx_record_decl; + + //printf ("parent = %s\n", parent_record_decl->getNameAsCString()); + + //const Decl *root_cdecl = cxx_record_decl->getCanonicalDecl(); + // Didn't find things easily, lets let clang do its thang... + IdentifierInfo & ident_ref = ast_context->Idents.get(name, name + strlen (name)); + DeclarationName decl_name(&ident_ref); + + CXXBasePaths paths; + if (cxx_record_decl->lookupInBases(CXXRecordDecl::FindOrdinaryMember, + decl_name.getAsOpaquePtr(), + paths)) + { + uint32_t child_idx; + CXXBasePaths::const_paths_iterator path, path_end = paths.end(); + for (path = paths.begin(); path != path_end; ++path) + { + const size_t num_path_elements = path->size(); + for (size_t e=0; e(elem.Base->getType()->getAs()->getDecl()); + } + } + DeclContext::lookup_iterator named_decl_pos; + for (named_decl_pos = path->Decls.first; + named_decl_pos != path->Decls.second && parent_record_decl; + ++named_decl_pos) + { + //printf ("path[%zu] = %s\n", child_indexes.size(), (*named_decl_pos)->getNameAsCString()); + + child_idx = GetIndexForRecordChild (parent_record_decl, *named_decl_pos, omit_empty_base_classes); + if (child_idx == UINT32_MAX) + { + child_indexes.clear(); + return 0; + } + else + { + child_indexes.push_back (child_idx); + } + } + } + return child_indexes.size(); + } + } + + } + break; + + case Type::ConstantArray: + { +// const ConstantArrayType *array = cast(parent_qual_type.getTypePtr()); +// const uint64_t element_count = array->getSize().getLimitedValue(); +// +// if (idx < element_count) +// { +// std::pair field_type_info = ast_context->getTypeInfo(array->getElementType()); +// +// char element_name[32]; +// ::snprintf (element_name, sizeof (element_name), "%s[%u]", parent_name ? parent_name : "", idx); +// +// child_name.assign(element_name); +// assert(field_type_info.first % 8 == 0); +// child_byte_size = field_type_info.first / 8; +// child_byte_offset = idx * child_byte_size; +// return array->getElementType().getAsOpaquePtr(); +// } + } + break; + +// case Type::MemberPointerType: +// { +// MemberPointerType *mem_ptr_type = cast(qual_type.getTypePtr()); +// QualType pointee_type = mem_ptr_type->getPointeeType(); +// +// if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) +// { +// return GetIndexOfChildWithName (ast_context, +// mem_ptr_type->getPointeeType().getAsOpaquePtr(), +// name); +// } +// } +// break; +// + case Type::LValueReference: + case Type::RValueReference: + { + ReferenceType *reference_type = cast(qual_type.getTypePtr()); + QualType pointee_type = reference_type->getPointeeType(); + + if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + { + return GetIndexOfChildMemberWithName (ast_context, + reference_type->getPointeeType().getAsOpaquePtr(), + name, + omit_empty_base_classes, + child_indexes); + } + } + break; + + case Type::Pointer: + { + PointerType *pointer_type = cast(qual_type.getTypePtr()); + QualType pointee_type = pointer_type->getPointeeType(); + + if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + { + return GetIndexOfChildMemberWithName (ast_context, + pointer_type->getPointeeType().getAsOpaquePtr(), + name, + omit_empty_base_classes, + child_indexes); + } + else + { +// if (parent_name) +// { +// child_name.assign(1, '*'); +// child_name += parent_name; +// } +// +// // We have a pointer to an simple type +// if (idx == 0) +// { +// std::pair clang_type_info = ast_context->getTypeInfo(pointee_type); +// assert(clang_type_info.first % 8 == 0); +// child_byte_size = clang_type_info.first / 8; +// child_byte_offset = 0; +// return pointee_type.getAsOpaquePtr(); +// } + } + } + break; + + case Type::Typedef: + return GetIndexOfChildMemberWithName (ast_context, + cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr(), + name, + omit_empty_base_classes, + child_indexes); + + default: + break; + } + } + return 0; +} + + +// Get the index of the child of "clang_type" whose name matches. This function +// doesn't descend into the children, but only looks one level deep and name +// matches can include base class names. + +uint32_t +ClangASTContext::GetIndexOfChildWithName +( + ASTContext *ast_context, + void *clang_type, + const char *name, + bool omit_empty_base_classes +) +{ + if (clang_type && name && name[0]) + { + QualType qual_type(QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case Type::Record: + { + const RecordType *record_type = cast(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + + assert(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + + if (cxx_record_decl) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + // Skip empty base classes + CXXRecordDecl *base_class_decl = cast(base_class->getType()->getAs()->getDecl()); + if (omit_empty_base_classes && RecordHasFields(base_class_decl) == false) + continue; + + if (base_class->getType().getAsString().compare (name) == 0) + return child_idx; + ++child_idx; + } + } + + // Try and find a field that matches NAME + RecordDecl::field_iterator field, field_end; + StringRef name_sref(name); + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); + field != field_end; + ++field, ++child_idx) + { + if (field->getName().equals (name_sref)) + return child_idx; + } + + } + break; + + case Type::ConstantArray: + { +// const ConstantArrayType *array = cast(parent_qual_type.getTypePtr()); +// const uint64_t element_count = array->getSize().getLimitedValue(); +// +// if (idx < element_count) +// { +// std::pair field_type_info = ast_context->getTypeInfo(array->getElementType()); +// +// char element_name[32]; +// ::snprintf (element_name, sizeof (element_name), "%s[%u]", parent_name ? parent_name : "", idx); +// +// child_name.assign(element_name); +// assert(field_type_info.first % 8 == 0); +// child_byte_size = field_type_info.first / 8; +// child_byte_offset = idx * child_byte_size; +// return array->getElementType().getAsOpaquePtr(); +// } + } + break; + +// case Type::MemberPointerType: +// { +// MemberPointerType *mem_ptr_type = cast(qual_type.getTypePtr()); +// QualType pointee_type = mem_ptr_type->getPointeeType(); +// +// if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) +// { +// return GetIndexOfChildWithName (ast_context, +// mem_ptr_type->getPointeeType().getAsOpaquePtr(), +// name); +// } +// } +// break; +// + case Type::LValueReference: + case Type::RValueReference: + { + ReferenceType *reference_type = cast(qual_type.getTypePtr()); + QualType pointee_type = reference_type->getPointeeType(); + + if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + { + return GetIndexOfChildWithName (ast_context, + reference_type->getPointeeType().getAsOpaquePtr(), + name, + omit_empty_base_classes); + } + } + break; + + case Type::Pointer: + { + PointerType *pointer_type = cast(qual_type.getTypePtr()); + QualType pointee_type = pointer_type->getPointeeType(); + + if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + { + return GetIndexOfChildWithName (ast_context, + pointer_type->getPointeeType().getAsOpaquePtr(), + name, + omit_empty_base_classes); + } + else + { +// if (parent_name) +// { +// child_name.assign(1, '*'); +// child_name += parent_name; +// } +// +// // We have a pointer to an simple type +// if (idx == 0) +// { +// std::pair clang_type_info = ast_context->getTypeInfo(pointee_type); +// assert(clang_type_info.first % 8 == 0); +// child_byte_size = clang_type_info.first / 8; +// child_byte_offset = 0; +// return pointee_type.getAsOpaquePtr(); +// } + } + } + break; + + case Type::Typedef: + return GetIndexOfChildWithName (ast_context, + cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr(), + name, + omit_empty_base_classes); + + default: + break; + } + } + return UINT32_MAX; +} + +#pragma mark TagType + +bool +ClangASTContext::SetTagTypeKind (void *tag_clang_type, int kind) +{ + if (tag_clang_type) + { + QualType tag_qual_type(QualType::getFromOpaquePtr(tag_clang_type)); + Type *clang_type = tag_qual_type.getTypePtr(); + if (clang_type) + { + TagType *tag_type = dyn_cast(clang_type); + if (tag_type) + { + TagDecl *tag_decl = dyn_cast(tag_type->getDecl()); + if (tag_decl) + { + tag_decl->setTagKind ((TagDecl::TagKind)kind); + return true; + } + } + } + } + return false; +} + + +#pragma mark DeclContext Functions + +DeclContext * +ClangASTContext::GetDeclContextForType (void *clang_type) +{ + if (clang_type == NULL) + return NULL; + + QualType qual_type(QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case Type::FunctionNoProto: break; + case Type::FunctionProto: break; + case Type::IncompleteArray: break; + case Type::VariableArray: break; + case Type::ConstantArray: break; + case Type::ExtVector: break; + case Type::Vector: break; + case Type::Builtin: break; + case Type::ObjCObjectPointer: break; + case Type::BlockPointer: break; + case Type::Pointer: break; + case Type::LValueReference: break; + case Type::RValueReference: break; + case Type::MemberPointer: break; + case Type::Complex: break; + case Type::ObjCInterface: break; + case Type::Record: + return cast(qual_type)->getDecl(); + case Type::Enum: + return cast(qual_type)->getDecl(); + case Type::Typedef: + return ClangASTContext::GetDeclContextForType (cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr()); + + case Type::TypeOfExpr: break; + case Type::TypeOf: break; + case Type::Decltype: break; + //case Type::QualifiedName: break; + case Type::TemplateSpecialization: break; + } + // No DeclContext in this type... + return NULL; +} + +#pragma mark Namespace Declarations + +NamespaceDecl * +ClangASTContext::GetUniqueNamespaceDeclaration (const char *name, const Declaration &decl, DeclContext *decl_ctx) +{ + // TODO: Do something intelligent with the Declaration object passed in + // like maybe filling in the SourceLocation with it... + if (name) + { + ASTContext *ast_context = getASTContext(); + if (decl_ctx == NULL) + decl_ctx = ast_context->getTranslationUnitDecl(); + return NamespaceDecl::Create(*ast_context, decl_ctx, SourceLocation(), &ast_context->Idents.get(name)); + } + return NULL; +} + + +#pragma mark Function Types + +FunctionDecl * +ClangASTContext::CreateFunctionDeclaration (const char *name, void *function_clang_type, int storage, bool is_inline) +{ + if (name) + { + ASTContext *ast_context = getASTContext(); + assert (ast_context != NULL); + + if (name && name[0]) + { + return FunctionDecl::Create(*ast_context, + ast_context->getTranslationUnitDecl(), + SourceLocation(), + DeclarationName (&ast_context->Idents.get(name)), + QualType::getFromOpaquePtr(function_clang_type), + NULL, + (FunctionDecl::StorageClass)storage, + (FunctionDecl::StorageClass)storage, + is_inline); + } + else + { + return FunctionDecl::Create(*ast_context, + ast_context->getTranslationUnitDecl(), + SourceLocation(), + DeclarationName (), + QualType::getFromOpaquePtr(function_clang_type), + NULL, + (FunctionDecl::StorageClass)storage, + (FunctionDecl::StorageClass)storage, + is_inline); + } + } + return NULL; +} + +void * +ClangASTContext::CreateFunctionType (void *result_type, void **args, unsigned num_args, bool isVariadic, unsigned TypeQuals) +{ + ASTContext *ast_context = getASTContext(); + assert (ast_context != NULL); + std::vector qual_type_args; + for (unsigned i=0; igetFunctionType(QualType::getFromOpaquePtr(result_type), + qual_type_args.data(), + qual_type_args.size(), + isVariadic, + TypeQuals, + false, // hasExceptionSpec + false, // hasAnyExceptionSpec, + 0, // NumExs + 0, // const QualType *ExArray + FunctionType::ExtInfo ()).getAsOpaquePtr(); // NoReturn); +} + +ParmVarDecl * +ClangASTContext::CreateParmeterDeclaration (const char *name, void * return_type, int storage) +{ + ASTContext *ast_context = getASTContext(); + assert (ast_context != NULL); + return ParmVarDecl::Create(*ast_context, + ast_context->getTranslationUnitDecl(), + SourceLocation(), + name && name[0] ? &ast_context->Idents.get(name) : NULL, + QualType::getFromOpaquePtr(return_type), + NULL, + (VarDecl::StorageClass)storage, + (VarDecl::StorageClass)storage, + 0); +} + +void +ClangASTContext::SetFunctionParameters (FunctionDecl *function_decl, ParmVarDecl **params, unsigned num_params) +{ + if (function_decl) + function_decl->setParams (params, num_params); +} + + +#pragma mark Array Types + +void * +ClangASTContext::CreateArrayType (void *element_type, size_t element_count, uint32_t bit_stride) +{ + if (element_type) + { + ASTContext *ast_context = getASTContext(); + assert (ast_context != NULL); + llvm::APInt ap_element_count (64, element_count); + return ast_context->getConstantArrayType(QualType::getFromOpaquePtr(element_type), + ap_element_count, + ArrayType::Normal, + 0).getAsOpaquePtr(); // ElemQuals + } + return NULL; +} + + +#pragma mark TagDecl + +bool +ClangASTContext::StartTagDeclarationDefinition (void *clang_type) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + Type *t = qual_type.getTypePtr(); + if (t) + { + TagType *tag_type = dyn_cast(t); + if (tag_type) + { + TagDecl *tag_decl = tag_type->getDecl(); + if (tag_decl) + { + tag_decl->startDefinition(); + return true; + } + } + } + } + return false; +} + +bool +ClangASTContext::CompleteTagDeclarationDefinition (void *clang_type) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + Type *t = qual_type.getTypePtr(); + if (t) + { + TagType *tag_type = dyn_cast(t); + if (tag_type) + { + TagDecl *tag_decl = tag_type->getDecl(); + if (tag_decl) + { + tag_decl->completeDefinition(); + return true; + } + } + } + } + return false; +} + + +#pragma mark Enumeration Types + +void * +ClangASTContext::CreateEnumerationType (const Declaration &decl, const char *name) +{ + // TODO: Do something intelligent with the Declaration object passed in + // like maybe filling in the SourceLocation with it... + ASTContext *ast_context = getASTContext(); + assert (ast_context != NULL); + EnumDecl *enum_decl = EnumDecl::Create(*ast_context, + ast_context->getTranslationUnitDecl(), + SourceLocation(), + name && name[0] ? &ast_context->Idents.get(name) : NULL, + SourceLocation(), + NULL); + if (enum_decl) + return ast_context->getTagDeclType(enum_decl).getAsOpaquePtr(); + return NULL; +} + +bool +ClangASTContext::AddEnumerationValueToEnumerationType +( + void *enum_clang_type, + void *enumerator_clang_type, + const Declaration &decl, + const char *name, + int64_t enum_value, + uint32_t enum_value_bit_size +) +{ + if (enum_clang_type && enumerator_clang_type && name) + { + // TODO: Do something intelligent with the Declaration object passed in + // like maybe filling in the SourceLocation with it... + ASTContext *ast_context = getASTContext(); + IdentifierTable *identifier_table = getIdentifierTable(); + + assert (ast_context != NULL); + assert (identifier_table != NULL); + QualType enum_qual_type (QualType::getFromOpaquePtr(enum_clang_type)); + + Type *clang_type = enum_qual_type.getTypePtr(); + if (clang_type) + { + const EnumType *enum_type = dyn_cast(clang_type); + + if (enum_type) + { + llvm::APSInt enum_llvm_apsint(enum_value_bit_size, false); + enum_llvm_apsint = enum_value; + EnumConstantDecl *enumerator_decl = + EnumConstantDecl::Create(*ast_context, + enum_type->getDecl(), + SourceLocation(), + name ? &identifier_table->get(name) : NULL, // Identifier + QualType::getFromOpaquePtr(enumerator_clang_type), + NULL, + enum_llvm_apsint); + + if (enumerator_decl) + { + enum_type->getDecl()->addDecl(enumerator_decl); + return true; + } + } + } + } + return false; +} + +#pragma mark Pointers & References + +void * +ClangASTContext::CreatePointerType (void *clang_type) +{ + if (clang_type) + return getASTContext()->getPointerType(QualType::getFromOpaquePtr(clang_type)).getAsOpaquePtr(); + return NULL; +} + +void * +ClangASTContext::CreateLValueReferenceType (void *clang_type) +{ + if (clang_type) + return getASTContext()->getLValueReferenceType (QualType::getFromOpaquePtr(clang_type)).getAsOpaquePtr(); + return NULL; +} + +void * +ClangASTContext::CreateRValueReferenceType (void *clang_type) +{ + if (clang_type) + return getASTContext()->getRValueReferenceType (QualType::getFromOpaquePtr(clang_type)).getAsOpaquePtr(); + return NULL; +} + +size_t +ClangASTContext::GetPointerBitSize () +{ + ASTContext *ast_context = getASTContext(); + return ast_context->getTypeSize(ast_context->VoidPtrTy); +} + +bool +ClangASTContext::IsPointerOrReferenceType (void *clang_type, void **target_type) +{ + if (clang_type == NULL) + return false; + + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case Type::ObjCObjectPointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::BlockPointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::Pointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::MemberPointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::LValueReference: + if (target_type) + *target_type = cast(qual_type)->desugar().getAsOpaquePtr(); + return true; + case Type::RValueReference: + if (target_type) + *target_type = cast(qual_type)->desugar().getAsOpaquePtr(); + return true; + case Type::Typedef: + return ClangASTContext::IsPointerOrReferenceType (cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr()); + default: + break; + } + return false; +} + +size_t +ClangASTContext::GetTypeBitSize (clang::ASTContext *ast_context, void *clang_type) +{ + if (clang_type) + return ast_context->getTypeSize(QualType::getFromOpaquePtr(clang_type)); + return 0; +} + +size_t +ClangASTContext::GetTypeBitAlign (clang::ASTContext *ast_context, void *clang_type) +{ + if (clang_type) + return ast_context->getTypeAlign(QualType::getFromOpaquePtr(clang_type)); + return 0; +} + +bool +ClangASTContext::IsIntegerType (void * clang_type, bool &is_signed) +{ + if (!clang_type) + return false; + + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + const BuiltinType *builtin_type = dyn_cast(qual_type->getCanonicalTypeInternal()); + + if (builtin_type) + { + if (builtin_type->isInteger()) + is_signed = builtin_type->isSignedInteger(); + + return true; + } + + return false; +} + +bool +ClangASTContext::IsPointerType (void *clang_type, void **target_type) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case Type::ObjCObjectPointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::BlockPointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::Pointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::MemberPointer: + if (target_type) + *target_type = cast(qual_type)->getPointeeType().getAsOpaquePtr(); + return true; + case Type::Typedef: + return ClangASTContext::IsPointerOrReferenceType (cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr(), target_type); + default: + break; + } + } + return false; +} + +bool +ClangASTContext::IsFloatingPointType (void *clang_type, uint32_t &count, bool &is_complex) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + + if (const BuiltinType *BT = dyn_cast(qual_type->getCanonicalTypeInternal())) + { + clang::BuiltinType::Kind kind = BT->getKind(); + if (kind >= BuiltinType::Float && kind <= BuiltinType::LongDouble) + { + count = 1; + is_complex = false; + return true; + } + } + else if (const ComplexType *CT = dyn_cast(qual_type->getCanonicalTypeInternal())) + { + if (IsFloatingPointType(CT->getElementType().getAsOpaquePtr(), count, is_complex)) + { + count = 2; + is_complex = true; + return true; + } + } + else if (const VectorType *VT = dyn_cast(qual_type->getCanonicalTypeInternal())) + { + if (IsFloatingPointType(VT->getElementType().getAsOpaquePtr(), count, is_complex)) + { + count = VT->getNumElements(); + is_complex = false; + return true; + } + } + } + return false; +} + + +bool +ClangASTContext::IsCStringType (void *clang_type, uint32_t &length) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case Type::ConstantArray: + { + ConstantArrayType *array = cast(qual_type.getTypePtr()); + QualType element_qual_type = array->getElementType(); + Type *canonical_type = element_qual_type->getCanonicalTypeInternal().getTypePtr(); + if (canonical_type && canonical_type->isCharType()) + { + // We know the size of the array and it could be a C string + // since it is an array of characters + length = array->getSize().getLimitedValue(); + return true; + } + } + break; + + case Type::Pointer: + { + PointerType *pointer_type = cast(qual_type.getTypePtr()); + Type *pointee_type_ptr = pointer_type->getPointeeType().getTypePtr(); + if (pointee_type_ptr) + { + Type *canonical_type_ptr = pointee_type_ptr->getCanonicalTypeInternal().getTypePtr(); + length = 0; // No length info, read until a NULL terminator is received + if (canonical_type_ptr) + return canonical_type_ptr->isCharType(); + else + return pointee_type_ptr->isCharType(); + } + } + break; + + case Type::Typedef: + return ClangASTContext::IsCStringType (cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr(), length); + + case Type::LValueReference: + case Type::RValueReference: + { + ReferenceType *reference_type = cast(qual_type.getTypePtr()); + Type *pointee_type_ptr = reference_type->getPointeeType().getTypePtr(); + if (pointee_type_ptr) + { + Type *canonical_type_ptr = pointee_type_ptr->getCanonicalTypeInternal().getTypePtr(); + length = 0; // No length info, read until a NULL terminator is received + if (canonical_type_ptr) + return canonical_type_ptr->isCharType(); + else + return pointee_type_ptr->isCharType(); + } + } + break; + } + } + return false; +} + +bool +ClangASTContext::IsArrayType (void * clang_type, void **member_type, uint64_t *size) +{ + if (!clang_type) + return false; + + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + + switch (qual_type->getTypeClass()) + { + case Type::ConstantArray: + if (member_type) + *member_type = cast(qual_type)->getElementType().getAsOpaquePtr(); + if (size) + *size = cast(qual_type)->getSize().getLimitedValue(ULONG_LONG_MAX); + return true; + case Type::IncompleteArray: + if (member_type) + *member_type = cast(qual_type)->getElementType().getAsOpaquePtr(); + if (size) + *size = 0; + return true; + case Type::VariableArray: + if (member_type) + *member_type = cast(qual_type)->getElementType().getAsOpaquePtr(); + if (size) + *size = 0; + case Type::DependentSizedArray: + if (member_type) + *member_type = cast(qual_type)->getElementType().getAsOpaquePtr(); + if (size) + *size = 0; + return true; + } + return false; +} + + +#pragma mark Typedefs + +void * +ClangASTContext::CreateTypedefType (const char *name, void *clang_type, DeclContext *decl_ctx) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + ASTContext *ast_context = getASTContext(); + IdentifierTable *identifier_table = getIdentifierTable(); + assert (ast_context != NULL); + assert (identifier_table != NULL); + if (decl_ctx == NULL) + decl_ctx = ast_context->getTranslationUnitDecl(); + TypedefDecl *decl = TypedefDecl::Create(*ast_context, + decl_ctx, + SourceLocation(), + name ? &identifier_table->get(name) : NULL, // Identifier + ast_context->CreateTypeSourceInfo(qual_type)); + + // Get a uniqued QualType for the typedef decl type + return ast_context->getTypedefType (decl).getAsOpaquePtr(); + } + return NULL; +} + + +std::string +ClangASTContext::GetTypeName (void *opaque_qual_type) +{ + std::string return_name; + + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(opaque_qual_type)); + + const clang::TypedefType *typedef_type = qual_type->getAs(); + if (typedef_type) + { + const clang::TypedefDecl *typedef_decl = typedef_type->getDecl(); + return_name = typedef_decl->getQualifiedNameAsString(); + } + else + { + return_name = qual_type.getAsString(); + } + + return return_name; +} + +// Disable this for now since I can't seem to get a nicely formatted float +// out of the APFloat class without just getting the float, double or quad +// and then using a formatted print on it which defeats the purpose. We ideally +// would like to get perfect string values for any kind of float semantics +// so we can support remote targets. The code below also requires a patch to +// llvm::APInt. +//bool +//ClangASTContext::ConvertFloatValueToString (ASTContext *ast_context, void *clang_type, const uint8_t* bytes, size_t byte_size, int apint_byte_order, std::string &float_str) +//{ +// uint32_t count = 0; +// bool is_complex = false; +// if (ClangASTContext::IsFloatingPointType (clang_type, count, is_complex)) +// { +// unsigned num_bytes_per_float = byte_size / count; +// unsigned num_bits_per_float = num_bytes_per_float * 8; +// +// float_str.clear(); +// uint32_t i; +// for (i=0; i 0) +// { +// if (i > 0) +// float_str.append(", "); +// float_str.append(s); +// if (i == 1 && is_complex) +// float_str.append(1, 'i'); +// } +// } +// return !float_str.empty(); +// } +// return false; +//} + +size_t +ClangASTContext::ConvertStringToFloatValue (ASTContext *ast_context, void *clang_type, const char *s, uint8_t *dst, size_t dst_size) +{ + if (clang_type) + { + QualType qual_type (QualType::getFromOpaquePtr(clang_type)); + uint32_t count = 0; + bool is_complex = false; + if (ClangASTContext::IsFloatingPointType (clang_type, count, is_complex)) + { + // TODO: handle complex and vector types + if (count != 1) + return false; + + StringRef s_sref(s); + APFloat ap_float(ast_context->getFloatTypeSemantics(qual_type), s_sref); + + const uint64_t bit_size = ast_context->getTypeSize (qual_type); + const uint64_t byte_size = bit_size / 8; + if (dst_size >= byte_size) + { + if (bit_size == sizeof(float)*8) + { + float float32 = ap_float.convertToFloat(); + ::memcpy (dst, &float32, byte_size); + return byte_size; + } + else if (bit_size >= 64) + { + llvm::APInt ap_int(ap_float.bitcastToAPInt()); + ::memcpy (dst, ap_int.getRawData(), byte_size); + return byte_size; + } + } + } + } + return 0; +} diff --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp new file mode 100644 index 000000000000..60a893b1d1e3 --- /dev/null +++ b/lldb/source/Symbol/CompileUnit.cpp @@ -0,0 +1,366 @@ +//===-- CompileUnit.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/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +using namespace lldb; +using namespace lldb_private; + +CompileUnit::CompileUnit (Module *module, void *user_data, const char *pathname, const lldb::user_id_t cu_sym_id, Language::Type language) : + ModuleChild(module), + FileSpec (pathname), + UserID(cu_sym_id), + Language (language), + m_user_data (user_data), + m_flags (0), + m_functions (), + m_support_files (), + m_line_table_ap (), + m_variables() +{ + assert(module != NULL); +} + +CompileUnit::CompileUnit (Module *module, void *user_data, const FileSpec &fspec, const lldb::user_id_t cu_sym_id, Language::Type language) : + ModuleChild(module), + FileSpec (fspec), + UserID(cu_sym_id), + Language (language), + m_user_data (user_data), + m_flags (0), + m_functions (), + m_support_files (), + m_line_table_ap (), + m_variables() +{ + assert(module != NULL); +} + +CompileUnit::~CompileUnit () +{ +} + +void +CompileUnit::CalculateSymbolContext(SymbolContext* sc) +{ + sc->comp_unit = this; + GetModule()->CalculateSymbolContext(sc); +} + +void +CompileUnit::DumpSymbolContext(Stream *s) +{ + GetModule()->DumpSymbolContext(s); + s->Printf(", CompileUnit{0x%8.8x}", GetID()); +} + + + +//---------------------------------------------------------------------- +// Dump the current contents of this object. No functions that cause on +// demand parsing of functions, globals, statics are called, so this +// is a good function to call to get an idea of the current contents of +// the CompileUnit object. +//---------------------------------------------------------------------- +void +CompileUnit::Dump(Stream *s, bool show_context) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + *s << "CompileUnit" << (const UserID&)*this + << ", language = " << (const Language&)*this + << ", file='" << (const FileSpec&)*this << "'\n"; + +// m_types.Dump(s); + + if (m_variables.get()) + { + s->IndentMore(); + m_variables->Dump(s, show_context); + s->IndentLess(); + } + + if (!m_functions.empty()) + { + s->IndentMore(); + std::vector::const_iterator pos; + std::vector::const_iterator end = m_functions.end(); + for (pos = m_functions.begin(); pos != end; ++pos) + { + (*pos)->Dump(s, show_context); + } + + s->IndentLess(); + s->EOL(); + } +} + +//---------------------------------------------------------------------- +// Add a function to this compile unit +//---------------------------------------------------------------------- +void +CompileUnit::AddFunction(FunctionSP& funcSP) +{ + // TODO: order these by address + m_functions.push_back(funcSP); +} + +FunctionSP +CompileUnit::GetFunctionAtIndex (size_t idx) +{ + FunctionSP funcSP; + if (idx < m_functions.size()) + funcSP = m_functions[idx]; + return funcSP; +} + +//---------------------------------------------------------------------- +// Find functions using the a Mangled::Tokens token list. This +// function currently implements an interative approach designed to find +// all instances of certain functions. It isn't designed to the the +// quickest way to lookup functions as it will need to iterate through +// all functions and see if they match, though it does provide a powerful +// and context sensitive way to search for all functions with a certain +// name, all functions in a namespace, or all functions of a template +// type. See Mangled::Tokens::Parse() comments for more information. +// +// The function prototype will need to change to return a list of +// results. It was originally used to help debug the Mangled class +// and the Mangled::Tokens::MatchesQuery() function and it currently +// will print out a list of matching results for the functions that +// are currently in this compile unit. +// +// A FindFunctions method should be called prior to this that takes +// a regular function name (const char * or ConstString as a parameter) +// before resorting to this slower but more complete function. The +// other FindFunctions method should be able to take advantage of any +// accelerator tables available in the debug information (which is +// parsed by the SymbolFile parser plug-ins and registered with each +// Module). +//---------------------------------------------------------------------- +//void +//CompileUnit::FindFunctions(const Mangled::Tokens& tokens) +//{ +// if (!m_functions.empty()) +// { +// Stream s(stdout); +// std::vector::const_iterator pos; +// std::vector::const_iterator end = m_functions.end(); +// for (pos = m_functions.begin(); pos != end; ++pos) +// { +// const ConstString& demangled = (*pos)->Mangled().Demangled(); +// if (demangled) +// { +// const Mangled::Tokens& func_tokens = (*pos)->Mangled().GetTokens(); +// if (func_tokens.MatchesQuery (tokens)) +// s << "demangled MATCH found: " << demangled << "\n"; +// } +// } +// } +//} + +FunctionSP +CompileUnit::FindFunctionByUID (lldb::user_id_t func_uid) +{ + FunctionSP funcSP; + if (!m_functions.empty()) + { + std::vector::const_iterator pos; + std::vector::const_iterator end = m_functions.end(); + for (pos = m_functions.begin(); pos != end; ++pos) + { + if ((*pos)->GetID() == func_uid) + { + funcSP = *pos; + break; + } + } + } + return funcSP; +} + + +LineTable* +CompileUnit::GetLineTable() +{ + if (m_line_table_ap.get() == NULL) + { + if (m_flags.IsClear(flagsParsedLineTable)) + { + m_flags.Set(flagsParsedLineTable); + SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + symbol_vendor->ParseCompileUnitLineTable(sc); + } + } + } + return m_line_table_ap.get(); +} + +void +CompileUnit::SetLineTable(LineTable* line_table) +{ + if (line_table == NULL) + m_flags.Clear(flagsParsedLineTable); + else + m_flags.Set(flagsParsedLineTable); + m_line_table_ap.reset(line_table); +} + +VariableListSP +CompileUnit::GetVariableList(bool can_create) +{ + if (m_variables.get() == NULL && can_create) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + assert(sc.module_sp); + sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc); + } + + return m_variables; +} + +uint32_t +CompileUnit::FindLineEntry (uint32_t start_idx, uint32_t line, const FileSpec* file_spec_ptr, LineEntry *line_entry_ptr) +{ + uint32_t file_idx = 0; + + if (file_spec_ptr) + { + file_idx = GetSupportFiles().FindFileIndex (1, *file_spec_ptr); + if (file_idx == UINT32_MAX) + return UINT32_MAX; + } + else + { + // All the line table entries actually point to the version of the Compile + // Unit that is in the support files (the one at 0 was artifically added.) + // So prefer the one further on in the support files if it exists... + FileSpecList &support_files = GetSupportFiles(); + file_idx = support_files.FindFileIndex (1, support_files.GetFileSpecAtIndex(0)); + if (file_idx == UINT32_MAX) + file_idx = 0; + } + LineTable *line_table = GetLineTable(); + if (line_table) + return line_table->FindLineEntryIndexByFileIndex (start_idx, file_idx, line, true, line_entry_ptr); + return UINT32_MAX; +} + + + + +uint32_t +CompileUnit::ResolveSymbolContext +( + const FileSpec& file_spec, + uint32_t line, + bool check_inlines, + bool exact, + uint32_t resolve_scope, + SymbolContextList &sc_list +) +{ + const uint32_t prev_size = sc_list.GetSize(); + bool file_spec_matches_cu_file_spec = FileSpec::Compare(file_spec, this, !file_spec.GetDirectory().IsEmpty()) == 0; + if (check_inlines || file_spec_matches_cu_file_spec) + { + SymbolContext sc(GetModule()); + sc.comp_unit = this; + + uint32_t file_idx = UINT32_MAX; + + // If we are looking for inline functions only and we don't + // find it in the support files, we are done. + + if (check_inlines) + { + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec); + if (file_idx == UINT32_MAX) + return 0; + } + + if (line != 0) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table != NULL) + { + // We will have already looked up the file index if + // we are searching for inline entries. + if (!check_inlines) + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec); + + if (file_idx != UINT32_MAX) + { + uint32_t found_line; + + uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_idx, line, exact, &sc.line_entry); + found_line = sc.line_entry.line; + + while (line_idx != UINT_MAX) + { + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_idx, found_line, true, &sc.line_entry); + } + } + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + + } + return sc_list.GetSize() - prev_size; +} + +void +CompileUnit::SetVariableList(VariableListSP &variables) +{ + m_variables = variables; +} + +FileSpecList& +CompileUnit::GetSupportFiles () +{ + if (m_support_files.GetSize() == 0) + { + if (m_flags.IsClear(flagsParsedSupportFiles)) + { + m_flags.Set(flagsParsedSupportFiles); + SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + symbol_vendor->ParseCompileUnitSupportFiles(sc, m_support_files); + } + } + } + return m_support_files; +} + +void * +CompileUnit::GetUserData () const +{ + return m_user_data; +} + + diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp new file mode 100644 index 000000000000..febca9241375 --- /dev/null +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -0,0 +1,1344 @@ +//===-- DWARFCallFrameInfo.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 +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +static void +DumpRegisterName (Stream *s, Thread *thread, const ArchSpec *arch, uint32_t reg_kind, uint32_t reg_num) +{ + const char *reg_name = NULL; + RegisterContext *reg_ctx = NULL; + if (thread) + { + reg_ctx = thread->GetRegisterContext(); + if (reg_ctx) + reg_name = reg_ctx->GetRegisterName (reg_ctx->ConvertRegisterKindToRegisterNumber (reg_kind, reg_num)); + } + + if (reg_name == NULL && arch != NULL) + { + switch (reg_kind) + { + case eRegisterKindDWARF: reg_name = arch->GetRegisterName(reg_num, eRegisterKindDWARF); break; + case eRegisterKindGCC: reg_name = arch->GetRegisterName(reg_num, eRegisterKindGCC); break; + default: + break; + } + } + + if (reg_name) + s->PutCString(reg_name); + else + { + const char *reg_kind_name = NULL; + switch (reg_kind) + { + case eRegisterKindDWARF: reg_kind_name = "dwarf-reg"; break; + case eRegisterKindGCC: reg_kind_name = "compiler-reg"; break; + case eRegisterKindGeneric: reg_kind_name = "generic-reg"; break; + default: + break; + } + if (reg_kind_name) + s->Printf("%s(%u)", reg_kind_name, reg_num); + else + s->Printf("reg(%d.%u)", reg_kind, reg_num); + } +} + + +#pragma mark DWARFCallFrameInfo::RegisterLocation + +DWARFCallFrameInfo::RegisterLocation::RegisterLocation() : + m_type(isSame) +{ +} + + +bool +DWARFCallFrameInfo::RegisterLocation::operator == (const DWARFCallFrameInfo::RegisterLocation& rhs) const +{ + if (m_type != rhs.m_type) + return false; + switch (m_type) + { + case unspecified: + case isUndefined: + case isSame: + return true; + + case atCFAPlusOffset: + return m_location.offset == rhs.m_location.offset; + + case isCFAPlusOffset: + return m_location.offset == rhs.m_location.offset; + + case inOtherRegister: + return m_location.reg_num == rhs.m_location.reg_num; + + default: + break; + } + return false; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetUnspecified() +{ + m_type = unspecified; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetUndefined() +{ + m_type = isUndefined; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetSame() +{ + m_type = isSame; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetAtCFAPlusOffset(int64_t offset) +{ + m_type = atCFAPlusOffset; + m_location.offset = offset; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetIsCFAPlusOffset(int64_t offset) +{ + m_type = isCFAPlusOffset; + m_location.offset = offset; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetInRegister (uint32_t reg_num) +{ + m_type = inOtherRegister; + m_location.reg_num = reg_num; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetAtDWARFExpression(const uint8_t *opcodes, uint32_t len) +{ + m_type = atDWARFExpression; + m_location.expr.opcodes = opcodes; + m_location.expr.length = len; +} + +void +DWARFCallFrameInfo::RegisterLocation::SetIsDWARFExpression(const uint8_t *opcodes, uint32_t len) +{ + m_type = isDWARFExpression; + m_location.expr.opcodes = opcodes; + m_location.expr.length = len; +} + +void +DWARFCallFrameInfo::RegisterLocation::Dump(Stream *s, const DWARFCallFrameInfo &cfi, Thread *thread, const Row *row, uint32_t reg_num) const +{ + const ArchSpec *arch = cfi.GetArchitecture(); + const uint32_t reg_kind = cfi.GetRegisterKind(); + + DumpRegisterName (s, thread, arch, reg_kind, reg_num); + s->PutChar('='); + + switch (m_type) + { + case unspecified: + s->PutChar('?'); + break; + + case isUndefined: + s->PutCString("undefined"); + break; + + case isSame: + s->PutCString("same"); + break; + + case atCFAPlusOffset: + s->PutChar('['); + // Fall through to isCFAPlusOffset... + case isCFAPlusOffset: + { + DumpRegisterName (s, thread, arch, reg_kind, row->GetCFARegister()); + int32_t offset = row->GetCFAOffset() + m_location.offset; + if (offset != 0) + s->Printf("%-+d", offset); + if (m_type == atCFAPlusOffset) + s->PutChar(']'); + } + break; + + case inOtherRegister: + DumpRegisterName (s, thread, arch, reg_kind, m_location.reg_num); + break; + + case atDWARFExpression: + s->PutCString("[EXPR] "); + break; + + case isDWARFExpression: + s->PutCString("EXPR "); + break; + } +} + + +#pragma mark DWARFCallFrameInfo::Row + +DWARFCallFrameInfo::Row::Row() : + m_offset(0), + m_cfa_reg_num(0), + m_cfa_offset(0), + m_register_locations() +{ +} + +DWARFCallFrameInfo::Row::~Row() +{ +} + +void +DWARFCallFrameInfo::Row::Clear() +{ + m_register_locations.clear(); +} +bool +DWARFCallFrameInfo::Row::GetRegisterInfo (uint32_t reg_num, DWARFCallFrameInfo::RegisterLocation& register_location) const +{ + collection::const_iterator pos = m_register_locations.find(reg_num); + if (pos != m_register_locations.end()) + { + register_location = pos->second; + return true; + } + return false; +} + +void +DWARFCallFrameInfo::Row::SetRegisterInfo (uint32_t reg_num, const RegisterLocation& register_location) +{ + m_register_locations[reg_num] = register_location; +} + + +void +DWARFCallFrameInfo::Row::Dump(Stream* s, const DWARFCallFrameInfo &cfi, Thread *thread, lldb::addr_t base_addr) const +{ + const ArchSpec *arch = cfi.GetArchitecture(); + const uint32_t reg_kind = cfi.GetRegisterKind(); + collection::const_iterator pos, end = m_register_locations.end(); + s->Indent(); + s->Printf("0x%16.16llx: CFA=", m_offset + base_addr); + DumpRegisterName(s, thread, arch, reg_kind, m_cfa_reg_num); + if (m_cfa_offset != 0) + s->Printf("%-+lld", m_cfa_offset); + + for (pos = m_register_locations.begin(); pos != end; ++pos) + { + s->PutChar(' '); + pos->second.Dump(s, cfi, thread, this, pos->first); + } + s->EOL(); +} + + +#pragma mark DWARFCallFrameInfo::FDE + + +DWARFCallFrameInfo::FDE::FDE (dw_offset_t offset, const AddressRange &range) : + m_fde_offset (offset), + m_range (range), + m_row_list () +{ +} + +DWARFCallFrameInfo::FDE::~FDE() +{ +} + +void +DWARFCallFrameInfo::FDE::AppendRow (const Row &row) +{ + if (m_row_list.empty() || m_row_list.back().GetOffset() != row.GetOffset()) + m_row_list.push_back(row); + else + m_row_list.back() = row; +} + +void +DWARFCallFrameInfo::FDE::Dump (Stream *s, const DWARFCallFrameInfo &cfi, Thread* thread) const +{ + s->Indent(); + s->Printf("FDE{0x%8.8x} ", m_fde_offset); + m_range.Dump(s, NULL, Address::DumpStyleFileAddress); + lldb::addr_t fde_base_addr = m_range.GetBaseAddress().GetFileAddress(); + s->EOL(); + s->IndentMore(); + collection::const_iterator pos, end = m_row_list.end(); + for (pos = m_row_list.begin(); pos != end; ++pos) + { + pos->Dump(s, cfi, thread, fde_base_addr); + } + s->IndentLess(); +} + +const AddressRange & +DWARFCallFrameInfo::FDE::GetAddressRange() const +{ + return m_range; +} + +bool +DWARFCallFrameInfo::FDE::IsValidRowIndex (uint32_t idx) const +{ + return idx < m_row_list.size(); +} + +const DWARFCallFrameInfo::Row& +DWARFCallFrameInfo::FDE::GetRowAtIndex (uint32_t idx) +{ + // You must call IsValidRowIndex(idx) first before calling this!!! + return m_row_list[idx]; +} +#pragma mark DWARFCallFrameInfo::FDEInfo + +DWARFCallFrameInfo::FDEInfo::FDEInfo () : + fde_offset (0), + fde_sp() +{ +} + +DWARFCallFrameInfo::FDEInfo::FDEInfo (off_t offset) : + fde_offset(offset), + fde_sp() +{ +} + +#pragma mark DWARFCallFrameInfo::CIE + +DWARFCallFrameInfo::CIE::CIE(dw_offset_t offset) : + cie_offset (offset), + version (0), + augmentation(), + code_align (0), + data_align (0), + return_addr_reg_num (0), + inst_offset (0), + inst_length (0), + ptr_encoding (DW_GNU_EH_PE_absptr) +{ +} + + +DWARFCallFrameInfo::CIE::~CIE() +{ +} + +void +DWARFCallFrameInfo::CIE::Dump(Stream *s, Thread* thread, const ArchSpec *arch, uint32_t reg_kind) const +{ + s->Indent(); + s->Printf("CIE{0x%8.8x} version=%u, code_align=%u, data_align=%d, return_addr_reg=", cie_offset, version, code_align, data_align); + DumpRegisterName(s, thread, arch, reg_kind, return_addr_reg_num); + s->Printf(", instr_offset=0x%8.8x, instr_length=%u, ptr_encoding=0x%02x\n", + inst_offset, + inst_length, + ptr_encoding); +} + +#pragma mark DWARFCallFrameInfo::CIE + +DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile *objfile, Section *section, uint32_t reg_kind) : + m_objfile (objfile), + m_section (section), + m_reg_kind (reg_kind), // The flavor of registers that the CFI data uses (One of the defines that starts with "LLDB_REGKIND_") + m_cfi_data (), + m_cie_map (), + m_fde_map () +{ + if (objfile && section) + { + section->ReadSectionDataFromObjectFile (objfile, m_cfi_data); + } +} + +DWARFCallFrameInfo::~DWARFCallFrameInfo() +{ +} + +bool +DWARFCallFrameInfo::IsEHFrame() const +{ + return (m_reg_kind == eRegisterKindGCC); +} + +const ArchSpec * +DWARFCallFrameInfo::GetArchitecture() const +{ + if (m_objfile && m_objfile->GetModule()) + return &m_objfile->GetModule()->GetArchitecture(); + return NULL; +} + +uint32_t +DWARFCallFrameInfo::GetRegisterKind () const +{ + return m_reg_kind; +} + +void +DWARFCallFrameInfo::SetRegisterKind (uint32_t reg_kind) +{ + m_reg_kind = reg_kind; +} + + + + +const DWARFCallFrameInfo::CIE* +DWARFCallFrameInfo::GetCIE(dw_offset_t cie_offset) +{ + Index (); + + cie_map_t::iterator pos = m_cie_map.find(cie_offset); + + if (pos != m_cie_map.end()) + { + // Parse and cache the CIE + if (pos->second.get() == NULL) + pos->second = ParseCIE (cie_offset); + + return pos->second.get(); + } + return NULL; +} + +DWARFCallFrameInfo::CIE::shared_ptr +DWARFCallFrameInfo::ParseCIE (const dw_offset_t cie_offset) +{ + CIE::shared_ptr cie_sp(new CIE(cie_offset)); + const bool for_eh_frame = IsEHFrame(); + dw_offset_t offset = cie_offset; + const uint32_t length = m_cfi_data.GetU32(&offset); + const dw_offset_t cie_id = m_cfi_data.GetU32(&offset); + const dw_offset_t end_offset = cie_offset + length + 4; + if (length > 0 && (!for_eh_frame && cie_id == 0xfffffffful) || (for_eh_frame && cie_id == 0ul)) + { + size_t i; + // cie.offset = cie_offset; + // cie.length = length; + // cie.cieID = cieID; + cie_sp->ptr_encoding = DW_GNU_EH_PE_absptr; + cie_sp->version = m_cfi_data.GetU8(&offset); + + for (i=0; iaugmentation[i] = m_cfi_data.GetU8(&offset); + if (cie_sp->augmentation[i] == '\0') + { + // Zero out remaining bytes in augmentation string + for (size_t j = i+1; jaugmentation[j] = '\0'; + + break; + } + } + + if (i == CFI_AUG_MAX_SIZE && cie_sp->augmentation[CFI_AUG_MAX_SIZE-1] != '\0') + { + fprintf(stderr, "CIE parse error: CIE augmentation string was too large for the fixed sized buffer of %d bytes.\n", CFI_AUG_MAX_SIZE); + return cie_sp; + } + cie_sp->code_align = (uint32_t)m_cfi_data.GetULEB128(&offset); + cie_sp->data_align = (int32_t)m_cfi_data.GetSLEB128(&offset); + cie_sp->return_addr_reg_num = m_cfi_data.GetU8(&offset); + + if (cie_sp->augmentation[0]) + { + // Get the length of the eh_frame augmentation data + // which starts with a ULEB128 length in bytes + const size_t aug_data_len = (size_t)m_cfi_data.GetULEB128(&offset); + const size_t aug_data_end = offset + aug_data_len; + const size_t aug_str_len = strlen(cie_sp->augmentation); + // A 'z' may be present as the first character of the string. + // If present, the Augmentation Data field shall be present. + // The contents of the Augmentation Data shall be intepreted + // according to other characters in the Augmentation String. + if (cie_sp->augmentation[0] == 'z') + { + // Extract the Augmentation Data + size_t aug_str_idx = 0; + for (aug_str_idx = 1; aug_str_idx < aug_str_len; aug_str_idx++) + { + char aug = cie_sp->augmentation[aug_str_idx]; + switch (aug) + { + case 'L': + // Indicates the presence of one argument in the + // Augmentation Data of the CIE, and a corresponding + // argument in the Augmentation Data of the FDE. The + // argument in the Augmentation Data of the CIE is + // 1-byte and represents the pointer encoding used + // for the argument in the Augmentation Data of the + // FDE, which is the address of a language-specific + // data area (LSDA). The size of the LSDA pointer is + // specified by the pointer encoding used. + m_cfi_data.GetU8(&offset); + break; + + case 'P': + // Indicates the presence of two arguments in the + // Augmentation Data of the cie_sp-> The first argument + // is 1-byte and represents the pointer encoding + // used for the second argument, which is the + // address of a personality routine handler. The + // size of the personality routine pointer is + // specified by the pointer encoding used. + { + uint8_t arg_ptr_encoding = m_cfi_data.GetU8(&offset); + m_cfi_data.GetGNUEHPointer(&offset, arg_ptr_encoding, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS); + } + break; + + case 'R': + // A 'R' may be present at any position after the + // first character of the string. The Augmentation + // Data shall include a 1 byte argument that + // represents the pointer encoding for the address + // pointers used in the FDE. + cie_sp->ptr_encoding = m_cfi_data.GetU8(&offset); + break; + } + } + } + else if (strcmp(cie_sp->augmentation, "eh") == 0) + { + // If the Augmentation string has the value "eh", then + // the EH Data field shall be present + } + + // Set the offset to be the end of the augmentation data just in case + // we didn't understand any of the data. + offset = (uint32_t)aug_data_end; + } + + if (end_offset > offset) + { + cie_sp->inst_offset = offset; + cie_sp->inst_length = end_offset - offset; + } + } + + return cie_sp; +} + +DWARFCallFrameInfo::FDE::shared_ptr +DWARFCallFrameInfo::ParseFDE(const dw_offset_t fde_offset) +{ + const bool for_eh_frame = IsEHFrame(); + FDE::shared_ptr fde_sp; + + dw_offset_t offset = fde_offset; + const uint32_t length = m_cfi_data.GetU32(&offset); + dw_offset_t cie_offset = m_cfi_data.GetU32(&offset); + const dw_offset_t end_offset = fde_offset + length + 4; + + // Translate the CIE_id from the eh_frame format, which + // is relative to the FDE offset, into a __eh_frame section + // offset + if (for_eh_frame) + cie_offset = offset - (cie_offset + 4); + + const CIE* cie = GetCIE(cie_offset); + if (cie) + { + const lldb::addr_t pc_rel_addr = m_section->GetFileAddress(); + const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS; + const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr); + lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_GNU_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr); + + if (cie->augmentation[0] == 'z') + { + uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + offset += aug_data_len; + } + + AddressRange fde_range (range_base, range_len, m_objfile->GetSectionList ()); + fde_sp.reset(new FDE(fde_offset, fde_range)); + if (offset < end_offset) + { + dw_offset_t fde_instr_offset = offset; + uint32_t fde_instr_length = end_offset - offset; + if (cie->inst_length > 0) + ParseInstructions(cie, fde_sp.get(), cie->inst_offset, cie->inst_length); + ParseInstructions(cie, fde_sp.get(), fde_instr_offset, fde_instr_length); + } + } + return fde_sp; +} + +const DWARFCallFrameInfo::FDE * +DWARFCallFrameInfo::FindFDE(const Address &addr) +{ + Index (); + + VMRange find_range(addr.GetFileAddress(), 0); + fde_map_t::iterator pos = m_fde_map.lower_bound (find_range); + fde_map_t::iterator end = m_fde_map.end(); + + if (pos != end) + { + if (pos->first.Contains(find_range.GetBaseAddress())) + { + // Parse and cache the FDE if we already haven't + if (pos->second.fde_sp.get() == NULL) + pos->second.fde_sp = ParseFDE(pos->second.fde_offset); + + return pos->second.fde_sp.get(); + } + } + return NULL; +} + + +void +DWARFCallFrameInfo::Index () +{ + if (m_flags.IsClear(eFlagParsedIndex)) + { + m_flags.Set (eFlagParsedIndex); + const bool for_eh_frame = IsEHFrame(); + CIE::shared_ptr empty_cie_sp; + dw_offset_t offset = 0; + // Parse all of the CIEs first since we will need them to be able to + // properly parse the FDE addresses due to them possibly having + // GNU pointer encodings in their augmentations... + while (m_cfi_data.ValidOffsetForDataOfSize(offset, 8)) + { + const dw_offset_t curr_offset = offset; + const uint32_t length = m_cfi_data.GetU32(&offset); + const dw_offset_t next_offset = offset + length; + const dw_offset_t cie_id = m_cfi_data.GetU32(&offset); + + bool is_cie = for_eh_frame ? cie_id == 0 : cie_id == UINT32_MAX; + if (is_cie) + m_cie_map[curr_offset]= ParseCIE(curr_offset); + + offset = next_offset; + } + + // Now go back through and index all FDEs + offset = 0; + const lldb::addr_t pc_rel_addr = m_section->GetFileAddress(); + const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS; + const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; + while (m_cfi_data.ValidOffsetForDataOfSize(offset, 8)) + { + const dw_offset_t curr_offset = offset; + const uint32_t length = m_cfi_data.GetU32(&offset); + const dw_offset_t next_offset = offset + length; + const dw_offset_t cie_id = m_cfi_data.GetU32(&offset); + + bool is_fde = for_eh_frame ? cie_id != 0 : cie_id != UINT32_MAX; + if (is_fde) + { + dw_offset_t cie_offset; + if (for_eh_frame) + cie_offset = offset - (cie_id + 4); + else + cie_offset = cie_id; + + const CIE* cie = GetCIE(cie_offset); + assert(cie); + lldb::addr_t addr = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr); + lldb::addr_t length = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_GNU_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr); + m_fde_map[VMRange(addr, addr + length)] = FDEInfo(curr_offset); + } + + offset = next_offset; + } + } +} + +//---------------------------------------------------------------------- +// Parse instructions for a FDE. The initial instruction for the CIE +// are parsed first, then the instructions for the FDE are parsed +//---------------------------------------------------------------------- +void +DWARFCallFrameInfo::ParseInstructions(const CIE *cie, FDE *fde, dw_offset_t instr_offset, uint32_t instr_length) +{ + if (cie != NULL && fde == NULL) + return; + + uint32_t reg_num = 0; + int32_t op_offset = 0; + uint32_t tmp_uval32; + uint32_t code_align = cie->code_align; + int32_t data_align = cie->data_align; + typedef std::list RowStack; + + RowStack row_stack; + Row row; + if (fde->IsValidRowIndex(0)) + row = fde->GetRowAtIndex(0); + + dw_offset_t offset = instr_offset; + const dw_offset_t end_offset = instr_offset + instr_length; + RegisterLocation reg_location; + while (m_cfi_data.ValidOffset(offset) && offset < end_offset) + { + uint8_t inst = m_cfi_data.GetU8(&offset); + uint8_t primary_opcode = inst & 0xC0; + uint8_t extended_opcode = inst & 0x3F; + + if (primary_opcode) + { + switch (primary_opcode) + { + case DW_CFA_advance_loc : // (Row Creation Instruction) + { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta + // takes a single argument that represents a constant delta. The + // required action is to create a new table row with a location + // value that is computed by taking the current entry's location + // value and adding (delta * code_align). All other + // values in the new row are initially identical to the current row. + fde->AppendRow(row); + row.SlideOffset(extended_opcode * code_align); + } + break; + + case DW_CFA_offset : + { // 0x80 - high 2 bits are 0x2, lower 6 bits are register + // takes two arguments: an unsigned LEB128 constant representing a + // factored offset and a register number. The required action is to + // change the rule for the register indicated by the register number + // to be an offset(N) rule with a value of + // (N = factored offset * data_align). + reg_num = extended_opcode; + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; + reg_location.SetAtCFAPlusOffset(op_offset); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_restore : + { // 0xC0 - high 2 bits are 0x3, lower 6 bits are register + // takes a single argument that represents a register number. The + // required action is to change the rule for the indicated register + // to the rule assigned it by the initial_instructions in the CIE. + reg_num = extended_opcode; + // We only keep enough register locations around to + // unwind what is in our thread, and these are organized + // by the register index in that state, so we need to convert our + // GCC register number from the EH frame info, to a registe index + + if (fde->IsValidRowIndex(0) && fde->GetRowAtIndex(0).GetRegisterInfo(reg_num, reg_location)) + row.SetRegisterInfo (reg_num, reg_location); + } + break; + } + } + else + { + switch (extended_opcode) + { + case DW_CFA_nop : // 0x0 + break; + + case DW_CFA_set_loc : // 0x1 (Row Creation Instruction) + { + // DW_CFA_set_loc takes a single argument that represents an address. + // The required action is to create a new table row using the + // specified address as the location. All other values in the new row + // are initially identical to the current row. The new location value + // should always be greater than the current one. + fde->AppendRow(row); + row.SetOffset(m_cfi_data.GetPointer(&offset) - fde->GetAddressRange().GetBaseAddress().GetFileAddress()); + } + break; + + case DW_CFA_advance_loc1 : // 0x2 (Row Creation Instruction) + { + // takes a single uword argument that represents a constant delta. + // This instruction is identical to DW_CFA_advance_loc except for the + // encoding and size of the delta argument. + fde->AppendRow(row); + row.SlideOffset (m_cfi_data.GetU8(&offset) * code_align); + } + break; + + case DW_CFA_advance_loc2 : // 0x3 (Row Creation Instruction) + { + // takes a single uword argument that represents a constant delta. + // This instruction is identical to DW_CFA_advance_loc except for the + // encoding and size of the delta argument. + fde->AppendRow(row); + row.SlideOffset (m_cfi_data.GetU16(&offset) * code_align); + } + break; + + case DW_CFA_advance_loc4 : // 0x4 (Row Creation Instruction) + { + // takes a single uword argument that represents a constant delta. + // This instruction is identical to DW_CFA_advance_loc except for the + // encoding and size of the delta argument. + fde->AppendRow(row); + row.SlideOffset (m_cfi_data.GetU32(&offset) * code_align); + } + break; + + case DW_CFA_offset_extended : // 0x5 + { + // takes two unsigned LEB128 arguments representing a register number + // and a factored offset. This instruction is identical to DW_CFA_offset + // except for the encoding and size of the register argument. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; + reg_location.SetAtCFAPlusOffset(op_offset); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_restore_extended : // 0x6 + { + // takes a single unsigned LEB128 argument that represents a register + // number. This instruction is identical to DW_CFA_restore except for + // the encoding and size of the register argument. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + if (fde->IsValidRowIndex(0) && fde->GetRowAtIndex(0).GetRegisterInfo(reg_num, reg_location)) + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_undefined : // 0x7 + { + // takes a single unsigned LEB128 argument that represents a register + // number. The required action is to set the rule for the specified + // register to undefined. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + reg_location.SetUndefined(); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_same_value : // 0x8 + { + // takes a single unsigned LEB128 argument that represents a register + // number. The required action is to set the rule for the specified + // register to same value. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + reg_location.SetSame(); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_register : // 0x9 + { + // takes two unsigned LEB128 arguments representing register numbers. + // The required action is to set the rule for the first register to be + // the second register. + + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + uint32_t other_reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + reg_location.SetInRegister(other_reg_num); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_remember_state : // 0xA + // These instructions define a stack of information. Encountering the + // DW_CFA_remember_state instruction means to save the rules for every + // register on the current row on the stack. Encountering the + // DW_CFA_restore_state instruction means to pop the set of rules off + // the stack and place them in the current row. (This operation is + // useful for compilers that move epilogue code into the body of a + // function.) + row_stack.push_back(row); + break; + + case DW_CFA_restore_state : // 0xB + // These instructions define a stack of information. Encountering the + // DW_CFA_remember_state instruction means to save the rules for every + // register on the current row on the stack. Encountering the + // DW_CFA_restore_state instruction means to pop the set of rules off + // the stack and place them in the current row. (This operation is + // useful for compilers that move epilogue code into the body of a + // function.) + { + row = row_stack.back(); + row_stack.pop_back(); + } + break; + + case DW_CFA_def_cfa : // 0xC (CFA Definition Instruction) + { + // Takes two unsigned LEB128 operands representing a register + // number and a (non-factored) offset. The required action + // is to define the current CFA rule to use the provided + // register and offset. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); + row.SetCFARegister (reg_num); + row.SetCFAOffset (op_offset); + } + break; + + case DW_CFA_def_cfa_register : // 0xD (CFA Definition Instruction) + { + // takes a single unsigned LEB128 argument representing a register + // number. The required action is to define the current CFA rule to + // use the provided register (but to keep the old offset). + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + row.SetCFARegister (reg_num); + } + break; + + case DW_CFA_def_cfa_offset : // 0xE (CFA Definition Instruction) + { + // Takes a single unsigned LEB128 operand representing a + // (non-factored) offset. The required action is to define + // the current CFA rule to use the provided offset (but + // to keep the old register). + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); + row.SetCFAOffset (op_offset); + } + break; + + case DW_CFA_def_cfa_expression : // 0xF (CFA Definition Instruction) + { + size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset); + offset += (uint32_t)block_len; + } + break; + + case DW_CFA_expression : // 0x10 + { + // Takes two operands: an unsigned LEB128 value representing + // a register number, and a DW_FORM_block value representing a DWARF + // expression. The required action is to change the rule for the + // register indicated by the register number to be an expression(E) + // rule where E is the DWARF expression. That is, the DWARF + // expression computes the address. The value of the CFA is + // pushed on the DWARF evaluation stack prior to execution of + // the DWARF expression. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + const uint8_t *block_data = (uint8_t *)m_cfi_data.GetData(&offset, block_len); + + reg_location.SetAtDWARFExpression(block_data, block_len); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_offset_extended_sf : // 0x11 + { + // takes two operands: an unsigned LEB128 value representing a + // register number and a signed LEB128 factored offset. This + // instruction is identical to DW_CFA_offset_extended except + //that the second operand is signed and factored. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + reg_location.SetAtCFAPlusOffset(op_offset); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_def_cfa_sf : // 0x12 (CFA Definition Instruction) + { + // Takes two operands: an unsigned LEB128 value representing + // a register number and a signed LEB128 factored offset. + // This instruction is identical to DW_CFA_def_cfa except + // that the second operand is signed and factored. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + row.SetCFARegister (reg_num); + row.SetCFAOffset (op_offset); + } + break; + + case DW_CFA_def_cfa_offset_sf : // 0x13 (CFA Definition Instruction) + { + // takes a signed LEB128 operand representing a factored + // offset. This instruction is identical to DW_CFA_def_cfa_offset + // except that the operand is signed and factored. + op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + row.SetCFAOffset (op_offset); + } + break; + + case DW_CFA_val_expression : // 0x16 + { + // takes two operands: an unsigned LEB128 value representing a register + // number, and a DW_FORM_block value representing a DWARF expression. + // The required action is to change the rule for the register indicated + // by the register number to be a val_expression(E) rule where E is the + // DWARF expression. That is, the DWARF expression computes the value of + // the given register. The value of the CFA is pushed on the DWARF + // evaluation stack prior to execution of the DWARF expression. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + const uint8_t* block_data = (uint8_t*)m_cfi_data.GetData(&offset, block_len); +//#if defined(__i386__) || defined(__x86_64__) +// // The EH frame info for EIP and RIP contains code that looks for traps to +// // be a specific type and increments the PC. +// // For i386: +// // DW_CFA_val_expression where: +// // eip = DW_OP_breg6(+28), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x34), +// // DW_OP_deref, DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref, +// // DW_OP_dup, DW_OP_lit3, DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, +// // DW_OP_and, DW_OP_plus +// // This basically does a: +// // eip = ucontenxt.mcontext32->gpr.eip; +// // if (ucontenxt.mcontext32->exc.trapno != 3 && ucontenxt.mcontext32->exc.trapno != 4) +// // eip++; +// // +// // For x86_64: +// // DW_CFA_val_expression where: +// // rip = DW_OP_breg3(+48), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x90), DW_OP_deref, +// // DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref_size(4), DW_OP_dup, DW_OP_lit3, +// // DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, DW_OP_and, DW_OP_plus +// // This basically does a: +// // rip = ucontenxt.mcontext64->gpr.rip; +// // if (ucontenxt.mcontext64->exc.trapno != 3 && ucontenxt.mcontext64->exc.trapno != 4) +// // rip++; +// // The trap comparisons and increments are not needed as it hoses up the unwound PC which +// // is expected to point at least past the instruction that causes the fault/trap. So we +// // take it out by trimming the expression right at the first "DW_OP_swap" opcodes +// if (block_data != NULL && thread->GetPCRegNum(Thread::GCC) == reg_num) +// { +// if (thread->Is64Bit()) +// { +// if (block_len > 9 && block_data[8] == DW_OP_swap && block_data[9] == DW_OP_plus_uconst) +// block_len = 8; +// } +// else +// { +// if (block_len > 8 && block_data[7] == DW_OP_swap && block_data[8] == DW_OP_plus_uconst) +// block_len = 7; +// } +// } +//#endif + reg_location.SetIsDWARFExpression(block_data, block_len); + row.SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_val_offset : // 0x14 + case DW_CFA_val_offset_sf : // 0x15 + default: + tmp_uval32 = extended_opcode; + break; + } + } + } + fde->AppendRow(row); +} + +void +DWARFCallFrameInfo::ParseAll() +{ + Index(); + fde_map_t::iterator pos, end = m_fde_map.end(); + for (pos = m_fde_map.begin(); pos != end; ++ pos) + { + if (pos->second.fde_sp.get() == NULL) + pos->second.fde_sp = ParseFDE(pos->second.fde_offset); + } +} + + +//bool +//DWARFCallFrameInfo::UnwindRegisterAtIndex +//( +// const uint32_t reg_idx, +// const Thread* currState, +// const DWARFCallFrameInfo::Row* row, +// mapped_memory_t * memCache, +// Thread* unwindState +//) +//{ +// bool get_reg_success = false; +// +// const RegLocation* regLocation = row->regs.GetRegisterInfo(reg_idx); +// +// // On some systems, we may not get unwind info for the program counter, +// // but the return address register can be used to get that information. +// if (reg_idx == currState->GetPCRegNum(Thread::Index)) +// { +// const RegLocation* returnAddrRegLocation = row->regs.GetRegisterInfo(currState->GetRARegNum(Thread::Index)); +// if (regLocation == NULL) +// { +// // We have nothing to the program counter, so lets see if this +// // thread state has a return address (link register) that can +// // help us track down the previous PC +// regLocation = returnAddrRegLocation; +// } +// else if (regLocation->type == RegLocation::unspecified) +// { +// // We did have a location that didn't specify a value for unwinding +// // the PC, so if there is a info for the return return address +// // register (link register) lets use that +// if (returnAddrRegLocation) +// regLocation = returnAddrRegLocation; +// } +// } +// +// if (regLocation) +// { +// mach_vm_address_t unwoundRegValue = INVALID_VMADDR; +// switch (regLocation->type) +// { +// case RegLocation::undefined: +// // Register is not available, mark it as invalid +// unwindState->SetRegisterIsValid(reg_idx, Thread::Index, false); +// return true; +// +// case RegLocation::unspecified: +// // Nothing to do if it is the same +// return true; +// +// case RegLocation::same: +// // Nothing to do if it is the same +// return true; +// +// case RegLocation::atFPPlusOffset: +// case RegLocation::isFPPlusOffset: +// { +// uint64_t unwindAddress = currState->GetRegisterValue(row->cfa_register, Thread::GCC, INVALID_VMADDR, &get_reg_success); +// +// if (get_reg_success) +// { +// unwindAddress += row->cfa_offset + regLocation->location.offset; +// +// if (regLocation->type == RegLocation::isFPPlusOffset) +// { +// unwindState->SetRegisterValue(reg_idx, Thread::Index, unwindAddress); +// return true; +// } +// else +// { +// kern_return_t err = mapped_memory_read_pointer(memCache, unwindAddress, &unwoundRegValue); +// if (err != KERN_SUCCESS) +// { +// unwindState->SetRegisterIsValid(reg_idx, Thread::Index, false); +// return false; +// } +// unwindState->SetRegisterValue(reg_idx, Thread::Index, unwoundRegValue); +// return true; +// } +// } +// else +// { +// unwindState->SetRegisterIsValid(reg_idx, Thread::Index, false); +// } +// return false; +// } +// break; +// +// case RegLocation::atDWARFExpression: +// case RegLocation::isDWARFExpression: +// { +// bool swap = false; +// DWARFExpressionBaton baton = { currState, memCache, swap }; +// uint64_t expr_result = 0; +// CSBinaryDataRef opcodes(regLocation->location.expr.opcodes, regLocation->location.expr.length, swap); +// opcodes.SetPointerSize(currState->Is64Bit() ? 8 : 4); +// const char * expr_err = CSDWARFExpression::Evaluate(DWARFExpressionReadMemoryDCScriptInterpreter::Type, +// DWARFExpressionReadRegisterDCScriptInterpreter::Type, +// &baton, +// opcodes, +// 0, +// regLocation->location.expr.length, +// NULL, +// expr_result); +// if (expr_err == NULL) +// { +// // SUCCESS! +// if (regLocation->type == RegLocation::isDWARFExpression) +// { +// unwindState->SetRegisterValue(reg_idx, Thread::Index, expr_result); +// return true; +// } +// else +// { +// kern_return_t err = mapped_memory_read_pointer(memCache, expr_result, &unwoundRegValue); +// if (err != KERN_SUCCESS) +// { +// unwindState->SetRegisterIsValid(reg_idx, Thread::Index, false); +// return false; +// } +// unwindState->SetRegisterValue(reg_idx, Thread::Index, unwoundRegValue); +// return true; +// } +// } +// else +// { +// // FAIL +// unwindState->SetRegisterIsValid(reg_idx, Thread::Index, false); +// } +// return false; +// } +// break; +// +// +// case RegLocation::inRegister: +// // The value is in another register. +// unwoundRegValue = currState->GetRegisterValue(regLocation->location.reg, Thread::GCC, 0, &get_reg_success); +// if (get_reg_success) +// { +// unwindState->SetRegisterValue(reg_idx, Thread::Index, unwoundRegValue); +// return true; +// } +// return false; +// +// default: +// break; +// } +// } +// +// if (reg_idx == currState->GetSPRegNum(Thread::Index)) +// { +// uint64_t cfa = currState->GetRegisterValue(row->cfa_register, Thread::GCC, 0, &get_reg_success); +// if (get_reg_success) +// { +// return unwindState->SetSP(cfa + row->cfa_offset); +// } +// else +// { +// unwindState->SetRegisterIsValid(reg_idx, Thread::Index, false); +// return false; +// } +// } +// +// return false; +//} + +void +DWARFCallFrameInfo::Dump(Stream *s, Thread *thread) const +{ + s->Indent(); + s->Printf("DWARFCallFrameInfo for "); + *s << m_objfile->GetFileSpec(); + if (m_flags.IsSet(eFlagParsedIndex)) + { + s->Printf(" (CIE[%zu], FDE[%zu])\n", m_cie_map.size(), m_fde_map.size()); + s->IndentMore(); + cie_map_t::const_iterator cie_pos, cie_end = m_cie_map.end(); + const ArchSpec *arch = &m_objfile->GetModule()->GetArchitecture(); + + for (cie_pos = m_cie_map.begin(); cie_pos != cie_end; ++ cie_pos) + { + if (cie_pos->second.get() == NULL) + { + s->Indent(); + s->Printf("CIE{0x%8.8x} - unparsed\n", cie_pos->first); + } + else + { + cie_pos->second->Dump(s, thread, arch, m_reg_kind); + } + } + + fde_map_t::const_iterator fde_pos, fde_end = m_fde_map.end(); + for (fde_pos = m_fde_map.begin(); fde_pos != fde_end; ++ fde_pos) + { + if (fde_pos->second.fde_sp.get() == NULL) + { + s->Indent(); + s->Printf("FDE{0x%8.8x} - unparsed\n", fde_pos->second.fde_offset); + } + else + { + fde_pos->second.fde_sp->Dump(s, *this, thread); + } + } + s->IndentLess(); + } + else + { + s->PutCString(" (not indexed yet)\n"); + } +} + + +//uint32_t +//DWARFCallFrameInfo::UnwindThreadState(const Thread* currState, mapped_memory_t *memCache, bool is_first_frame, Thread* unwindState) +//{ +// if (currState == NULL || unwindState == NULL) +// return 0; +// +// *unwindState = *currState; +// uint32_t numRegisterUnwound = 0; +// uint64_t currPC = currState->GetPC(INVALID_VMADDR); +// +// if (currPC != INVALID_VMADDR) +// { +// // If this is not the first frame, we care about the previous instruction +// // since it will be at the instruction following the instruction that +// // made the function call. +// uint64_t unwindPC = currPC; +// if (unwindPC > 0 && !is_first_frame) +// --unwindPC; +// +//#if defined(__i386__) || defined(__x86_64__) +// // Only on i386 do we have __IMPORT segments that contain trampolines +// if (!currState->Is64Bit() && ImportRangesContainsAddress(unwindPC)) +// { +// uint64_t curr_sp = currState->GetSP(INVALID_VMADDR); +// mach_vm_address_t pc = INVALID_VMADDR; +// unwindState->SetSP(curr_sp + 4); +// kern_return_t err = mapped_memory_read_pointer(memCache, curr_sp, &pc); +// if (err == KERN_SUCCESS) +// { +// unwindState->SetPC(pc); +// return 2; +// } +// } +//#endif +// FDE *fde = FindFDE(unwindPC); +// if (fde) +// { +// FindRowUserData rowUserData (currState, unwindPC); +// ParseInstructions (currState, fde, FindRowForAddress, &rowUserData); +// +// const uint32_t numRegs = currState->NumRegisters(); +// for (uint32_t regNum = 0; regNum < numRegs; regNum++) +// { +// if (UnwindRegisterAtIndex(regNum, currState, &rowUserData.state, memCache, unwindState)) +// numRegisterUnwound++; +// } +// } +// } +// return numRegisterUnwound; +//} + + diff --git a/lldb/source/Symbol/Declaration.cpp b/lldb/source/Symbol/Declaration.cpp new file mode 100644 index 000000000000..a31a304b06f9 --- /dev/null +++ b/lldb/source/Symbol/Declaration.cpp @@ -0,0 +1,172 @@ +//===-- Declaration.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/Symbol/Declaration.h" +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +Declaration::Declaration() : + m_file(), + m_line(0), + m_column(0) +{ +} + +Declaration::Declaration(const FileSpec& f, uint32_t l, uint32_t c) : + m_file(f), + m_line(l), + m_column(c) +{ +} + +Declaration::Declaration(const Declaration& rhs) : + m_file(rhs.m_file), + m_line(rhs.m_line), + m_column(rhs.m_column) +{ +} + +Declaration::Declaration(const Declaration* decl_ptr) : + m_file(), + m_line(0), + m_column(0) +{ + if (decl_ptr != NULL) + *this = *decl_ptr; +} + +bool +Declaration::IsValid() const +{ + return m_file && m_line != 0; +} + +void +Declaration::Clear() +{ + m_file.Clear(); + m_line= 0; + m_column = 0; +} + +void +Declaration::Dump(Stream *s) const +{ + if (m_file) + { + *s << ", decl = '" << m_file; + if (m_line > 0) + s->Printf(":%u", m_line); + if (m_column > 0) + s->Printf(":%u", m_column); + s->PutChar('\''); + } + else + { + if (m_line > 0) + { + s->Printf(", line = %u", m_line); + if (m_column > 0) + s->Printf(":%u", m_column); + } + else if (m_column > 0) + s->Printf(", column = %u", m_column); + } +} + +void +Declaration::DumpStopContext (Stream *s) const +{ + if (m_file) + { + if (s->GetVerbose()) + *s << m_file; + else + m_file.GetFilename().Dump(s); + + if (m_line > 0) + s->Printf(":%u", m_line); + if (m_column > 0) + s->Printf(":%u", m_column); + } + else + { + s->Printf(" line %u", m_line); + if (m_column > 0) + s->Printf(":%u", m_column); + } +} + +uint32_t +Declaration::GetColumn() const +{ + return m_column; +} + +FileSpec& +Declaration::GetFile() +{ + return m_file; +} + +const FileSpec& +Declaration::GetFile() const +{ + return m_file; +} + +uint32_t +Declaration::GetLine() const +{ + return m_line; +} + +size_t +Declaration::MemorySize() const +{ + return sizeof(Declaration); +} + +void +Declaration::SetColumn(uint32_t col) +{ + m_column = col; +} + +void +Declaration::SetFile(const FileSpec& file) +{ + m_file = file; +} + +void +Declaration::SetLine(uint32_t line) +{ + m_line = line; +} + + + +int +Declaration::Compare(const Declaration& a, const Declaration& b) +{ + int result = FileSpec::Compare(a.m_file, b.m_file, true); + if (result) + return result; + if (a.m_line < b.m_line) + return -1; + else if (a.m_line > b.m_line) + return 1; + if (a.m_column < b.m_column) + return -1; + else if (a.m_column > b.m_column) + return 1; + return 0; +} diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp new file mode 100644 index 000000000000..51c449acd93d --- /dev/null +++ b/lldb/source/Symbol/Function.cpp @@ -0,0 +1,432 @@ +//===-- Function.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/Symbol/Function.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "clang/AST/Type.h" +#include "clang/AST/CanonicalType.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Basic function information is contained in the FunctionInfo class. +// It is designed to contain the name, linkage name, and declaration +// location. +//---------------------------------------------------------------------- +FunctionInfo::FunctionInfo (const char *name, const Declaration *decl_ptr) : + m_name(name), + m_declaration(decl_ptr) +{ +} + + +FunctionInfo::FunctionInfo (const ConstString& name, const Declaration *decl_ptr) : + m_name(name), + m_declaration(decl_ptr) +{ +} + + +FunctionInfo::~FunctionInfo() +{ +} + +void +FunctionInfo::Dump(Stream *s) const +{ + if (m_name) + *s << ", name = \"" << m_name << "\""; + m_declaration.Dump(s); +} + + +int +FunctionInfo::Compare(const FunctionInfo& a, const FunctionInfo& b) +{ + int result = ConstString::Compare(a.GetName(), b.GetName()); + if (result) + return result; + + return Declaration::Compare(a.m_declaration, b.m_declaration); +} + + +Declaration& +FunctionInfo::GetDeclaration() +{ + return m_declaration; +} + +const Declaration& +FunctionInfo::GetDeclaration() const +{ + return m_declaration; +} + +const ConstString& +FunctionInfo::GetName() const +{ + return m_name; +} + +size_t +FunctionInfo::MemorySize() const +{ + return m_name.MemorySize() + m_declaration.MemorySize(); +} + + +InlineFunctionInfo::InlineFunctionInfo +( + const char *name, + const char *mangled, + const Declaration *decl_ptr, + const Declaration *call_decl_ptr +) : + FunctionInfo(name, decl_ptr), + m_mangled(mangled, true), + m_call_decl (call_decl_ptr) +{ +} + +InlineFunctionInfo::InlineFunctionInfo +( + const ConstString& name, + const Mangled &mangled, + const Declaration *decl_ptr, + const Declaration *call_decl_ptr +) : + FunctionInfo(name, decl_ptr), + m_mangled(mangled), + m_call_decl (call_decl_ptr) +{ +} + +InlineFunctionInfo::~InlineFunctionInfo() +{ +} + +int +InlineFunctionInfo::Compare(const InlineFunctionInfo& a, const InlineFunctionInfo& b) +{ + + int result = FunctionInfo::Compare(a, b); + if (result) + return result; + // only compare the mangled names if both have them + return Mangled::Compare(a.m_mangled, a.m_mangled); +} + +void +InlineFunctionInfo::Dump(Stream *s) const +{ + FunctionInfo::Dump(s); + if (m_mangled) + m_mangled.Dump(s); +} + +void +InlineFunctionInfo::DumpStopContext (Stream *s) const +{ +// s->Indent("[inlined] "); + s->Indent(); + if (m_mangled) + s->PutCString (m_mangled.GetName().AsCString()); + else + s->PutCString (m_name.AsCString()); +} + +Declaration & +InlineFunctionInfo::GetCallSite () +{ + return m_call_decl; +} + +const Declaration & +InlineFunctionInfo::GetCallSite () const +{ + return m_call_decl; +} + + +Mangled& +InlineFunctionInfo::GetMangled() +{ + return m_mangled; +} + +const Mangled& +InlineFunctionInfo::GetMangled() const +{ + return m_mangled; +} + +size_t +InlineFunctionInfo::MemorySize() const +{ + return FunctionInfo::MemorySize() + m_mangled.MemorySize(); +} + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +Function::Function +( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t type_uid, + const Mangled &mangled, + Type * type, + const AddressRange& range +) : + UserID(func_uid), + m_comp_unit(comp_unit), + m_type_uid(type_uid), + m_type(type), + m_mangled(mangled), + m_blocks(this, range), + m_frame_base(), + m_flags(), + m_prologue_byte_size(0) +{ + assert(comp_unit != NULL); +} + +Function::Function +( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t type_uid, + const char *mangled, + Type *type, + const AddressRange &range +) : + UserID(func_uid), + m_comp_unit(comp_unit), + m_type_uid(type_uid), + m_type(type), + m_mangled(mangled, true), + m_blocks(this, range), + m_frame_base(), + m_flags(), + m_prologue_byte_size(0) +{ + assert(comp_unit != NULL); +} + + +Function::~Function() +{ +} + +const AddressRange & +Function::GetAddressRange() +{ + return GetBlocks(true).GetAddressRange(); +} + +BlockList & +Function::GetBlocks(bool can_create) +{ + if (m_blocks.IsEmpty() && can_create) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + assert(sc.module_sp); + sc.module_sp->GetSymbolVendor()->ParseFunctionBlocks(sc); + } + return m_blocks; +} + +CompileUnit* +Function::GetCompileUnit() +{ + return m_comp_unit; +} + +const CompileUnit* +Function::GetCompileUnit() const +{ + return m_comp_unit; +} + +void +Function::Dump(Stream *s, bool show_context) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + *s << "Function" << (const UserID&)*this; + + m_mangled.Dump(s); + +// FunctionInfo::Dump(s); + if (m_type) + { + *s << ", type = " << (void*)m_type; + /// << " ("; + ///m_type->DumpTypeName(s); + ///s->PutChar(')'); + } + else if (m_type_uid != LLDB_INVALID_UID) + *s << ", type_uid = " << m_type_uid; + + s->EOL(); + // Dump the root object + if (!m_blocks.IsEmpty()) + m_blocks.Dump(s, Block::RootID, INT_MAX, show_context); +} + + +void +Function::CalculateSymbolContext(SymbolContext* sc) +{ + sc->function = this; + m_comp_unit->CalculateSymbolContext(sc); +} + +void +Function::DumpSymbolContext(Stream *s) +{ + m_comp_unit->DumpSymbolContext(s); + s->Printf(", Function{0x%8.8x}", GetID()); +} + +size_t +Function::MemorySize () const +{ + size_t mem_size = sizeof(Function) + m_blocks.MemorySize(); + return mem_size; +} + +Type* +Function::GetType() +{ + return m_type; +} + +const Type* +Function::GetType() const +{ + return m_type; +} + +Type +Function::GetReturnType () +{ + clang::QualType clang_type (clang::QualType::getFromOpaquePtr(GetType()->GetOpaqueClangQualType())); + assert (clang_type->isFunctionType()); + clang::FunctionType *function_type = dyn_cast (clang_type); + clang::QualType fun_return_qualtype = function_type->getResultType(); + + const ConstString fun_return_name(Type::GetClangTypeName(fun_return_qualtype.getAsOpaquePtr())); + + SymbolContext sc; + CalculateSymbolContext (&sc); + // Null out everything below the CompUnit 'cause we don't actually know these. + + size_t bit_size = ClangASTContext::GetTypeBitSize ((GetType()->GetClangASTContext().getASTContext()), &fun_return_qualtype); + Type return_type (0, GetType()->GetSymbolFile(), fun_return_name, bit_size, sc.comp_unit, 0, Type::eTypeUIDSynthetic, Declaration(), fun_return_qualtype.getAsOpaquePtr()); + return return_type; +} + +int +Function::GetArgumentCount () +{ + clang::QualType clang_type (clang::QualType::getFromOpaquePtr(GetType()->GetOpaqueClangQualType())); + assert (clang_type->isFunctionType()); + if (!clang_type->isFunctionProtoType()) + return -1; + + const clang::FunctionProtoType *function_proto_type = dyn_cast(clang_type); + if (function_proto_type != NULL) + return function_proto_type->getNumArgs(); + + return 0; +} + +const Type +Function::GetArgumentTypeAtIndex (size_t idx) +{ + clang::QualType clang_type (clang::QualType::getFromOpaquePtr(GetType()->GetOpaqueClangQualType())); + assert (clang_type->isFunctionType()); + if (!clang_type->isFunctionProtoType()) + return Type(); + + const clang::FunctionProtoType *function_proto_type = dyn_cast(clang_type); + if (function_proto_type != NULL) + { + unsigned num_args = function_proto_type->getNumArgs(); + if (idx >= num_args) + return Type(); + clang::QualType arg_qualtype = (function_proto_type->arg_type_begin())[idx]; + + const ConstString arg_return_name(Type::GetClangTypeName(arg_qualtype.getAsOpaquePtr())); + SymbolContext sc; + CalculateSymbolContext (&sc); + // Null out everything below the CompUnit 'cause we don't actually know these. + + size_t bit_size = ClangASTContext::GetTypeBitSize ((GetType()->GetClangASTContext().getASTContext()), &arg_qualtype); + Type arg_type (0, GetType()->GetSymbolFile(), arg_return_name, bit_size, sc.comp_unit, 0, Type::eTypeUIDSynthetic, Declaration(), arg_qualtype.getAsOpaquePtr()); + return arg_type; + } + + return Type(); +} + +const char * +Function::GetArgumentNameAtIndex (size_t idx) +{ + clang::Type *clang_type = static_cast(GetType()->GetOpaqueClangQualType())->getTypePtr(); + assert (clang_type->isFunctionType()); + if (!clang_type->isFunctionProtoType()) + return NULL; + return NULL; +} + +bool +Function::IsVariadic () +{ + const clang::Type *clang_type = static_cast(GetType()->GetOpaqueClangQualType())->getTypePtr(); + assert (clang_type->isFunctionType()); + if (!clang_type->isFunctionProtoType()) + return false; + + const clang::FunctionProtoType *function_proto_type = dyn_cast(clang_type); + if (function_proto_type != NULL) + { + return function_proto_type->isVariadic(); + } + + return false; +} + +uint32_t +Function::GetPrologueByteSize () +{ + if (m_prologue_byte_size == 0 && m_flags.IsClear(flagsCalculatedPrologueSize)) + { + m_flags.Set(flagsCalculatedPrologueSize); + LineTable* line_table = m_comp_unit->GetLineTable (); + if (line_table) + { + LineEntry line_entry; + if (line_table->FindLineEntryByAddress(GetAddressRange().GetBaseAddress(), line_entry)) + m_prologue_byte_size = line_entry.range.GetByteSize(); + } + } + return m_prologue_byte_size; +} + + + diff --git a/lldb/source/Symbol/LineEntry.cpp b/lldb/source/Symbol/LineEntry.cpp new file mode 100644 index 000000000000..cdc3c54eaa3d --- /dev/null +++ b/lldb/source/Symbol/LineEntry.cpp @@ -0,0 +1,237 @@ +//===-- LineEntry.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/Symbol/LineEntry.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Process.h" + +using namespace lldb_private; + +LineEntry::LineEntry() : + range(), + file(), + line(0), + column(0), + is_start_of_statement(0), + is_start_of_basic_block(0), + is_terminal_entry(0), + is_prologue_end(0), + is_epilogue_begin(0) +{ +} + +LineEntry::LineEntry +( + lldb_private::Section *section, + lldb::addr_t section_offset, + lldb::addr_t byte_size, + const FileSpec &_file, + uint32_t _line, + uint16_t _column, + bool _is_start_of_statement, + bool _is_start_of_basic_block, + bool _is_prologue_end, + bool _is_epilogue_begin, + bool _is_terminal_entry +) : + range(section, section_offset, byte_size), + file(_file), + line(_line), + column(_column), + is_start_of_statement(_is_start_of_statement), + is_start_of_basic_block(_is_start_of_basic_block), + is_prologue_end(_is_prologue_end), + is_epilogue_begin(_is_epilogue_begin), + is_terminal_entry(_is_terminal_entry) +{ +} + +void +LineEntry::Clear() +{ + range.Clear(); + file.Clear(); + line = 0; + column = 0; + is_start_of_statement = 0; + is_start_of_basic_block = 0; + is_prologue_end = 0; + is_epilogue_begin = 0; + is_terminal_entry = 0; +} + + +bool +LineEntry::IsValid() const +{ + return range.GetBaseAddress().IsValid() && line != 0; +} + +bool +LineEntry::DumpStopContext(Stream *s) const +{ + bool result = false; + if (file) + { + file.Dump (s); + if (line) + s->PutChar(':'); + result = true; + } + if (line) + s->Printf ("%u", line); + else + result = false; + + return result; +} + +bool +LineEntry::Dump +( + Stream *s, + Process *process, + bool show_file, + Address::DumpStyle style, + Address::DumpStyle fallback_style, + bool show_range +) const +{ + if (show_range) + { + // Show address range + if (!range.Dump(s, process, style, fallback_style)) + return false; + } + else + { + // Show address only + if (!range.GetBaseAddress().Dump(s, + process, + style, + fallback_style)) + return false; + } + if (line) + s->Printf(", line = %u", line); + if (column) + s->Printf(", column = %u", column); + if (show_file) + { + *s << ", file = " << file; + } + if (is_start_of_statement) + *s << ", is_start_of_statement = TRUE"; + + if (is_start_of_basic_block) + *s << ", is_start_of_basic_block = TRUE"; + + if (is_prologue_end) + *s << ", is_prologue_end = TRUE"; + + if (is_epilogue_begin) + *s << ", is_epilogue_begin = TRUE"; + + if (is_terminal_entry) + *s << ", is_terminal_entry = TRUE"; + return true; +} + +bool +LineEntry::GetDescription (Stream *s, lldb::DescriptionLevel level, CompileUnit* cu, Process *process) const +{ + + if (level == lldb::eDescriptionLevelBrief || level == lldb::eDescriptionLevelFull) + { + // Show address only + range.GetBaseAddress().Dump(s, process, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + + if (file) + *s << ' ' << file; + + if (line) + { + s->Printf(":%u", line); + if (column) + s->Printf(":%u", column); + } + + if (level == lldb::eDescriptionLevelFull) + { + if (is_start_of_statement) + *s << ", is_start_of_statement = TRUE"; + + if (is_start_of_basic_block) + *s << ", is_start_of_basic_block = TRUE"; + + if (is_prologue_end) + *s << ", is_prologue_end = TRUE"; + + if (is_epilogue_begin) + *s << ", is_epilogue_begin = TRUE"; + + if (is_terminal_entry) + *s << ", is_terminal_entry = TRUE"; + } + else + { + if (is_terminal_entry) + s->EOL(); + } + } + else + { + return Dump (s, process, true, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, true); + } + return true; +} + + +bool +lldb_private::operator< (const LineEntry& a, const LineEntry& b) +{ + return LineEntry::Compare (a, b) < 0; +} + +int +LineEntry::Compare (const LineEntry& a, const LineEntry& b) +{ + int result = Address::CompareFileAddress (a.range.GetBaseAddress(), b.range.GetBaseAddress()); + if (result != 0) + return result; + + const lldb::addr_t a_byte_size = a.range.GetByteSize(); + const lldb::addr_t b_byte_size = b.range.GetByteSize(); + + if (a_byte_size < b_byte_size) + return -1; + if (a_byte_size > b_byte_size) + return +1; + + // Check for an end sequence entry mismatch after we have determined + // that the address values are equal. If one of the items is an end + // sequence, we don't care about the line, file, or column info. + if (a.is_terminal_entry > b.is_terminal_entry) + return -1; + if (a.is_terminal_entry < b.is_terminal_entry) + return +1; + + if (a.line < b.line) + return -1; + if (a.line > b.line) + return +1; + + if (a.column < b.column) + return -1; + if (a.column > b.column) + return +1; + + return FileSpec::Compare (a.file, b.file, true); +} + diff --git a/lldb/source/Symbol/LineTable.cpp b/lldb/source/Symbol/LineTable.cpp new file mode 100644 index 000000000000..326fd6e0e785 --- /dev/null +++ b/lldb/source/Symbol/LineTable.cpp @@ -0,0 +1,332 @@ +//===-- LineTable.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/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// LineTable constructor +//---------------------------------------------------------------------- +LineTable::LineTable(CompileUnit* comp_unit) : + m_comp_unit(comp_unit), + m_section_list(), + m_entries() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +LineTable::~LineTable() +{ +} + +//void +//LineTable::AddLineEntry(const LineEntry& entry) +//{ +// // Do a binary search for the correct entry and insert it +// m_line_entries.insert(std::upper_bound(m_line_entries.begin(), m_line_entries.end(), entry), entry); +//} + +void +LineTable::AppendLineEntry +( + SectionSP& section_sp, + lldb::addr_t section_offset, + 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 +) +{ + uint32_t sect_idx = m_section_list.AddUniqueSection (section_sp); + // Make sure we don't user more than 256 sections as that is all we have + // room for in the LineTable::Entry::m_sect_idx. If this assert fires, + // we will need to m_sect_idx have more bits... + assert((section_offset & 0xffffffffff000000ull) == 0); + Entry entry(sect_idx, section_offset, line, column, file_idx, is_start_of_statement, is_start_of_basic_block, is_prologue_end, is_epilogue_begin, is_terminal_entry); + m_entries.push_back (entry); +} + + +void +LineTable::InsertLineEntry +( + SectionSP& section_sp, + lldb::addr_t section_offset, + 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 +) +{ + SectionSP line_section_sp(section_sp); + const Section *linked_section = line_section_sp->GetLinkedSection(); + if (linked_section) + { + section_offset += line_section_sp->GetLinkedOffset(); + line_section_sp = linked_section->GetSharedPointer(); + assert(line_section_sp.get()); + } + + uint32_t sect_idx = m_section_list.AddUniqueSection (line_section_sp); + // Make sure we don't user more than 256 sections as that is all we have + // room for in the LineTable::Entry::m_sect_idx. If this assert fires, + // we will need to m_sect_idx have more bits... + assert((section_offset & 0xffffffffff000000ull) == 0); + Entry entry(sect_idx, section_offset, line, column, file_idx, is_start_of_statement, is_start_of_basic_block, is_prologue_end, is_epilogue_begin, is_terminal_entry); + + entry_collection::iterator begin_pos = m_entries.begin(); + entry_collection::iterator end_pos = m_entries.end(); + LineTable::Entry::LessThanBinaryPredicate less_than_bp(this); + entry_collection::iterator pos = upper_bound(begin_pos, end_pos, entry, less_than_bp); + +// Stream s(stdout); +// s << "\n\nBefore:\n"; +// Dump (&s, Address::DumpStyleFileAddress); + m_entries.insert(pos, entry); +// s << "After:\n"; +// Dump (&s, Address::DumpStyleFileAddress); +} + +//---------------------------------------------------------------------- +LineTable::Entry::LessThanBinaryPredicate::LessThanBinaryPredicate(LineTable *line_table) : + m_line_table (line_table) +{ +} + +bool +LineTable::Entry::LessThanBinaryPredicate::operator() (const LineTable::Entry& a, const LineTable::Entry& b) const +{ + if (a.sect_idx == b.sect_idx) + { + #define LT_COMPARE(a,b) if (a != b) return a < b + LT_COMPARE (a.sect_offset, b.sect_offset); + LT_COMPARE (a.line, b.line); + LT_COMPARE (a.column, b.column); + LT_COMPARE (a.is_start_of_statement, b.is_start_of_statement); + LT_COMPARE (a.is_start_of_basic_block, b.is_start_of_basic_block); + // b and a reversed on purpose below. + LT_COMPARE (b.is_prologue_end, a.is_prologue_end); + LT_COMPARE (a.is_epilogue_begin, b.is_epilogue_begin); + // b and a reversed on purpose below. + LT_COMPARE (b.is_terminal_entry, a.is_terminal_entry); + LT_COMPARE (a.file_idx, b.file_idx); + return false; + #undef LT_COMPARE; + } + + const Section *a_section = m_line_table->GetSectionForEntryIndex (a.sect_idx); + const Section *b_section = m_line_table->GetSectionForEntryIndex (b.sect_idx); + return Section::Compare(*a_section, *b_section) < 0; +}; + + +Section * +LineTable::GetSectionForEntryIndex (uint32_t idx) +{ + if (idx < m_section_list.GetSize()) + return m_section_list.GetSectionAtIndex(idx).get(); + return NULL; +} + +uint32_t +LineTable::GetSize() const +{ + return m_entries.size(); +} + +bool +LineTable::GetLineEntryAtIndex(uint32_t idx, LineEntry& line_entry) +{ + if (idx < m_entries.size()) + { + ConvertEntryAtIndexToLineEntry (idx, line_entry); + return true; + } + line_entry.Clear(); + return false; +} + +bool +LineTable::FindLineEntryByAddress (const Address &so_addr, LineEntry& line_entry, uint32_t *index_ptr) +{ + if (index_ptr != NULL ) + *index_ptr = UINT32_MAX; + + bool success = false; + uint32_t sect_idx = m_section_list.FindSectionIndex (so_addr.GetSection()); + if (sect_idx != UINT32_MAX) + { + Entry search_entry; + search_entry.sect_idx = sect_idx; + search_entry.sect_offset = so_addr.GetOffset(); + + entry_collection::const_iterator begin_pos = m_entries.begin(); + entry_collection::const_iterator end_pos = m_entries.end(); + entry_collection::const_iterator pos = lower_bound(begin_pos, end_pos, search_entry, Entry::EntryAddressLessThan); + if (pos != end_pos) + { + if (pos != begin_pos) + { + if (pos->sect_offset != search_entry.sect_offset) + --pos; + else if (pos->sect_offset == search_entry.sect_offset) + { + while (pos != begin_pos) + { + entry_collection::const_iterator prev_pos = pos - 1; + if (prev_pos->sect_idx == search_entry.sect_idx && + prev_pos->sect_offset == search_entry.sect_offset) + --pos; + else + break; + } + } + + } + uint32_t match_idx = std::distance (begin_pos, pos); + success = ConvertEntryAtIndexToLineEntry(match_idx, line_entry); + if (index_ptr != NULL && success) + *index_ptr = match_idx; + } + } + return success; +} + + +bool +LineTable::ConvertEntryAtIndexToLineEntry (uint32_t idx, LineEntry &line_entry) +{ + if (idx < m_entries.size()) + { + const Entry& entry = m_entries[idx]; + line_entry.range.GetBaseAddress().SetSection(m_section_list.GetSectionAtIndex (entry.sect_idx).get()); + line_entry.range.GetBaseAddress().SetOffset(entry.sect_offset); + if (!entry.is_terminal_entry && idx + 1 < m_entries.size()) + { + const Entry& next_entry = m_entries[idx+1]; + if (next_entry.sect_idx == entry.sect_idx) + { + line_entry.range.SetByteSize(next_entry.sect_offset - entry.sect_offset); + } + else + { + Address next_line_addr(m_section_list.GetSectionAtIndex (next_entry.sect_idx).get(), next_entry.sect_offset); + line_entry.range.SetByteSize(next_line_addr.GetFileAddress() - line_entry.range.GetBaseAddress().GetFileAddress()); + } + } + else + line_entry.range.SetByteSize(0); + line_entry.file = m_comp_unit->GetSupportFiles().GetFileSpecAtIndex (entry.file_idx); + line_entry.line = entry.line; + line_entry.column = entry.column; + line_entry.is_start_of_statement = entry.is_start_of_statement; + line_entry.is_start_of_basic_block = entry.is_start_of_basic_block; + line_entry.is_prologue_end = entry.is_prologue_end; + line_entry.is_epilogue_begin = entry.is_epilogue_begin; + line_entry.is_terminal_entry = entry.is_terminal_entry; + return true; + } + return false; +} + +uint32_t +LineTable::FindLineEntryIndexByFileIndex (uint32_t start_idx, uint32_t file_idx, uint32_t line, bool exact, LineEntry* line_entry_ptr) +{ + const size_t count = m_entries.size(); + size_t best_match = UINT_MAX; + + for (size_t idx = start_idx; idx < count; ++idx) + { + // Skip line table rows that terminate the previous row (is_terminal_entry is non-zero) + if (m_entries[idx].is_terminal_entry) + continue; + + if (m_entries[idx].file_idx != file_idx) + continue; + + // Exact match always wins. Otherwise try to find the closest line > the desired + // line. + // FIXME: Maybe want to find the line closest before and the line closest after and + // if they're not in the same function, don't return a match. + + if (m_entries[idx].line < line) + { + continue; + } + else if (m_entries[idx].line == line) + { + if (line_entry_ptr) + ConvertEntryAtIndexToLineEntry (idx, *line_entry_ptr); + return idx; + } + else if (!exact) + { + if (best_match == UINT32_MAX) + best_match = idx; + else if (m_entries[idx].line < m_entries[best_match].line) + best_match = idx; + } + } + + if (best_match != UINT_MAX) + { + if (line_entry_ptr) + ConvertEntryAtIndexToLineEntry (best_match, *line_entry_ptr); + return best_match; + } + return UINT_MAX; +} + +void +LineTable::Dump (Stream *s, Process *process, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_line_ranges) +{ + const size_t count = m_entries.size(); + LineEntry line_entry; + FileSpec prev_file; + for (size_t idx = 0; idx < count; ++idx) + { + ConvertEntryAtIndexToLineEntry (idx, line_entry); + line_entry.Dump (s, process, prev_file != line_entry.file, style, fallback_style, show_line_ranges); + s->EOL(); + prev_file = line_entry.file; + } +} + + +void +LineTable::GetDescription (Stream *s, Process *process, DescriptionLevel level) +{ + const size_t count = m_entries.size(); + LineEntry line_entry; + for (size_t idx = 0; idx < count; ++idx) + { + ConvertEntryAtIndexToLineEntry (idx, line_entry); + line_entry.GetDescription (s, level, m_comp_unit, process); + s->EOL(); + } +} + + + diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp new file mode 100644 index 000000000000..8d224488641a --- /dev/null +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -0,0 +1,92 @@ +//===-- ObjectFile.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/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Symbol/SymbolFile.h" + +using namespace lldb; +using namespace lldb_private; + +ObjectFile* +ObjectFile::FindPlugin (Module* module, const FileSpec* file, lldb::addr_t file_offset, lldb::addr_t file_size) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "ObjectFile::FindPlugin (module = %s/%s, file = %p, file_offset = 0x%z8.8x, file_size = 0x%z8.8x)", + module->GetFileSpec().GetDirectory().AsCString(), + module->GetFileSpec().GetFilename().AsCString(), + file, file_offset, file_size); + std::auto_ptr object_file_ap; + + if (module != NULL) + { + if (file) + { + if (file_size == 0) + file_size = file->GetByteSize(); + + if (file_size == 0) + { + // Check for archive file with format "/path/to/archive.a(object.o)" + char path_with_object[PATH_MAX*2]; + module->GetFileSpec().GetPath(path_with_object, sizeof(path_with_object)); + + RegularExpression g_object_regex("(.*)\\(([^\\)]+)\\)$"); + if (g_object_regex.Execute (path_with_object, 2)) + { + FileSpec archive_file; + std::string path; + std::string object; + if (g_object_regex.GetMatchAtIndex (path_with_object, 1, path) && + g_object_regex.GetMatchAtIndex (path_with_object, 2, object)) + { + archive_file.SetFile (path.c_str()); + file_size = archive_file.GetByteSize(); + if (file_size > 0) + module->SetFileSpecAndObjectName (archive_file, ConstString(object.c_str())); + } + } + } + + DataBufferSP file_header_data_sp(file->ReadFileContents(file_offset, 512)); + uint32_t idx; + + // Check if this is a normal object file by iterating through + // all object file plugin instances. + ObjectFileCreateInstance create_object_file_callback; + for (idx = 0; (create_object_file_callback = PluginManager::GetObjectFileCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + object_file_ap.reset (create_object_file_callback(module, file_header_data_sp, file, file_offset, file_size)); + if (object_file_ap.get()) + return object_file_ap.release(); + } + + // Check if this is a object container by iterating through + // all object container plugin instances and then trying to get + // an object file from the container. + ObjectContainerCreateInstance create_object_container_callback; + for (idx = 0; (create_object_container_callback = PluginManager::GetObjectContainerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::auto_ptr object_container_ap(create_object_container_callback(module, file_header_data_sp, file, file_offset, file_size)); + + if (object_container_ap.get()) + object_file_ap.reset (object_container_ap->GetObjectFile(file)); + + if (object_file_ap.get()) + return object_file_ap.release(); + } + } + } + return NULL; +} diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp new file mode 100644 index 000000000000..fb01fc176bde --- /dev/null +++ b/lldb/source/Symbol/Symbol.cpp @@ -0,0 +1,463 @@ +//===-- Symbol.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/Symbol/Symbol.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + + +Symbol::Symbol() : + UserID (), + m_mangled (), + m_type (eSymbolTypeInvalid), + m_type_data (0), + m_type_data_resolved (false), + m_is_synthetic (false), + m_is_debug (false), + m_is_external (false), + m_size_is_sibling (false), + m_size_is_synthesized (false), + m_searched_for_function (false), + m_addr_range (), + m_flags (), + m_function (NULL) +{ +} + +Symbol::Symbol +( + user_id_t symID, + const char *name, + bool name_is_mangled, + SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const Section* section, + addr_t offset, + uint32_t size, + uint32_t flags +) : + UserID (symID), + m_mangled (name, name_is_mangled), + m_type (type), + m_type_data (0), + m_type_data_resolved (false), + m_is_synthetic (is_artificial), + m_is_debug (is_debug), + m_is_external (external), + m_size_is_sibling (false), + m_size_is_synthesized (false), + m_searched_for_function (false), + m_addr_range (section, offset, size), + m_flags (flags), + m_function (NULL) +{ +} + +Symbol::Symbol +( + user_id_t symID, + const char *name, + bool name_is_mangled, + SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const AddressRange &range, + uint32_t flags +) : + UserID (symID), + m_mangled (name, name_is_mangled), + m_type (type), + m_type_data (0), + m_type_data_resolved (false), + m_is_synthetic (is_artificial), + m_is_debug (is_debug), + m_is_external (external), + m_size_is_sibling (false), + m_size_is_synthesized (false), + m_searched_for_function (false), + m_addr_range (range), + m_flags (flags), + m_function (NULL) +{ +} + +Symbol::Symbol(const Symbol& rhs): + UserID (rhs), + m_mangled (rhs.m_mangled), + m_type (rhs.m_type), + m_type_data (rhs.m_type_data), + m_type_data_resolved (rhs.m_type_data_resolved), + m_is_synthetic (rhs.m_is_synthetic), + m_is_debug (rhs.m_is_debug), + m_is_external (rhs.m_is_external), + m_size_is_sibling (rhs.m_size_is_sibling), + m_size_is_synthesized (false), + m_searched_for_function (false), + m_addr_range (rhs.m_addr_range), + m_flags (rhs.m_flags), + m_function (NULL) +{ +} + +const Symbol& +Symbol::operator= (const Symbol& rhs) +{ + if (this != &rhs) + { + UserID::operator= (rhs); + m_mangled = rhs.m_mangled; + m_type = rhs.m_type; + m_type_data = rhs.m_type_data; + m_type_data_resolved = rhs.m_type_data_resolved; + m_is_synthetic = rhs.m_is_synthetic; + m_is_debug = rhs.m_is_debug; + m_is_external = rhs.m_is_external; + m_size_is_sibling = rhs.m_size_is_sibling; + m_size_is_synthesized = rhs.m_size_is_sibling; + m_searched_for_function = rhs.m_searched_for_function; + m_addr_range = rhs.m_addr_range; + m_flags = rhs.m_flags; + m_function = rhs.m_function; + } + return *this; +} + +AddressRange & +Symbol::GetAddressRangeRef() +{ + return m_addr_range; +} + +const AddressRange & +Symbol::GetAddressRangeRef() const +{ + return m_addr_range; +} + +AddressRange * +Symbol::GetAddressRangePtr() +{ + if (m_addr_range.GetBaseAddress().GetSection()) + return &m_addr_range; + return NULL; +} + +const AddressRange * +Symbol::GetAddressRangePtr() const +{ + if (m_addr_range.GetBaseAddress().GetSection()) + return &m_addr_range; + return NULL; +} + +bool +Symbol::GetSizeIsSibling() const +{ + return m_size_is_sibling; +} + +bool +Symbol::GetSizeIsSynthesized() const +{ + return m_size_is_synthesized; +} + +uint32_t +Symbol::GetSiblingIndex() const +{ + return m_size_is_sibling ? m_addr_range.GetByteSize() : 0; +} + +uint32_t +Symbol::GetFlags() const +{ + return m_flags; +} + +void +Symbol::SetFlags (uint32_t flags) +{ + m_flags = flags; +} + +SymbolType +Symbol::GetType() const +{ + return m_type; +} + +void +Symbol::SetType(SymbolType type) +{ + m_type = type; +} + +bool +Symbol::IsSynthetic () const +{ + return m_is_synthetic; +} + +void +Symbol::SetIsSynthetic (bool b) +{ + m_is_synthetic = b; +} + +void +Symbol::SetSizeIsSynthesized(bool b) +{ + m_size_is_synthesized = b; +} + + +bool +Symbol::IsDebug() const +{ + return m_is_debug; +} + +void +Symbol::SetDebug (bool b) +{ + m_is_debug = b; +} + +bool +Symbol::IsExternal() const +{ + return m_is_external; +} + +void +Symbol::SetExternal(bool b) +{ + m_is_external = b; +} + +bool +Symbol::IsTrampoline () const +{ + return m_type == eSymbolTypeTrampoline; +} + +uint32_t +Symbol::GetByteSize() const +{ + return m_addr_range.GetByteSize(); +} + +void +Symbol::SetByteSize (uint32_t size) +{ + m_addr_range.SetByteSize(size); +} + +void +Symbol::SetSizeIsSibling (bool b) +{ + m_size_is_sibling = b; +} + +void +Symbol::Dump(Stream *s, Process *process, uint32_t index) const +{ +// s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); +// s->Indent(); +// s->Printf("Symbol[%5u] %6u %c%c %-12s ", + s->Printf("[%5u] %6u %c%c%c %-12s ", + index, + GetID(), + m_is_debug ? 'D' : ' ', + m_is_synthetic ? 'S' : ' ', + m_is_external ? 'X' : ' ', + GetTypeAsString()); + + const Section *section = m_addr_range.GetBaseAddress().GetSection(); + if (section != NULL) + { + if (!m_addr_range.GetBaseAddress().Dump(s, NULL, Address::DumpStyleFileAddress)) + s->Printf("%*s", 18, ""); + + s->PutChar(' '); + + if (!m_addr_range.GetBaseAddress().Dump(s, process, Address::DumpStyleLoadAddress)) + s->Printf("%*s", 18, ""); + + const char *format = m_size_is_sibling ? + " Sibling -> [%5llu] 0x%8.8x %s\n": + " 0x%16.16llx 0x%8.8x %s\n"; + s->Printf( format, + m_addr_range.GetByteSize(), + m_flags, + m_mangled.GetName().AsCString("")); + } + else + { + const char *format = m_size_is_sibling ? + "0x%16.16llx Sibling -> [%5llu] 0x%8.8x %s\n": + "0x%16.16llx 0x%16.16llx 0x%8.8x %s\n"; + s->Printf( format, + m_addr_range.GetBaseAddress().GetOffset(), + m_addr_range.GetByteSize(), + m_flags, + m_mangled.GetName().AsCString("")); + } +} + +const Mangled& +Symbol::GetMangled() const +{ + return m_mangled; +} + +Mangled& +Symbol::GetMangled() +{ + return m_mangled; +} + +Address & +Symbol::GetValue() +{ + return m_addr_range.GetBaseAddress(); +} + +const Address & +Symbol::GetValue() const +{ + return m_addr_range.GetBaseAddress(); +} + +void +Symbol::SetValue (Address &value) +{ + m_addr_range.GetBaseAddress() = value; +} + +Function * +Symbol::GetFunction () +{ + if (m_function == NULL && !m_searched_for_function) + { + m_searched_for_function = true; + Module *module = m_addr_range.GetBaseAddress().GetModule(); + if (module) + { + SymbolContext sc; + if (module->ResolveSymbolContextForAddress(m_addr_range.GetBaseAddress(), eSymbolContextFunction, sc)) + m_function = sc.function; + } + } + return m_function; +} + +uint32_t +Symbol::GetPrologueByteSize () +{ + if (m_type == eSymbolTypeCode || m_type == eSymbolTypeFunction) + { + if (!m_type_data_resolved) + { + m_type_data_resolved = true; + Module *module = m_addr_range.GetBaseAddress().GetModule(); + SymbolContext sc; + if (module && module->ResolveSymbolContextForAddress (m_addr_range.GetBaseAddress(), + eSymbolContextLineEntry, + sc)) + { + m_type_data = sc.line_entry.range.GetByteSize(); + } + else + { + // TODO: expose something in Process to figure out the + // size of a function prologue. + } + } + return m_type_data; + } + return 0; +} + +void +Symbol::SetValue (const AddressRange &range) +{ + m_addr_range = range; +} + + +void +Symbol::SetValue(addr_t value) +{ + m_addr_range.GetBaseAddress().SetSection(NULL); + m_addr_range.GetBaseAddress().SetOffset(value); +} + + +bool +Symbol::Compare(const ConstString& name, SymbolType type) const +{ + if (m_type == eSymbolTypeAny || m_type == type) + return m_mangled.GetMangledName() == name || m_mangled.GetDemangledName() == name; + return false; +} + +#define ENUM_TO_CSTRING(x) case eSymbolType##x: return #x; + +const char * +Symbol::GetTypeAsString() const +{ + switch (m_type) + { + ENUM_TO_CSTRING(Invalid); + ENUM_TO_CSTRING(Absolute); + ENUM_TO_CSTRING(Extern); + ENUM_TO_CSTRING(Code); + ENUM_TO_CSTRING(Data); + ENUM_TO_CSTRING(Trampoline); + ENUM_TO_CSTRING(Runtime); + ENUM_TO_CSTRING(Exception); + ENUM_TO_CSTRING(SourceFile); + ENUM_TO_CSTRING(HeaderFile); + ENUM_TO_CSTRING(ObjectFile); + ENUM_TO_CSTRING(Function); + ENUM_TO_CSTRING(FunctionEnd); + ENUM_TO_CSTRING(CommonBlock); + ENUM_TO_CSTRING(Block); + ENUM_TO_CSTRING(Static); + ENUM_TO_CSTRING(Global); + ENUM_TO_CSTRING(Local); + ENUM_TO_CSTRING(Param); + ENUM_TO_CSTRING(Variable); + ENUM_TO_CSTRING(VariableType); + ENUM_TO_CSTRING(LineEntry); + ENUM_TO_CSTRING(LineHeader); + ENUM_TO_CSTRING(ScopeBegin); + ENUM_TO_CSTRING(ScopeEnd); + ENUM_TO_CSTRING(Additional); + ENUM_TO_CSTRING(Compiler); + ENUM_TO_CSTRING(Instrumentation); + ENUM_TO_CSTRING(Undefined); + default: + break; + } + return ""; +} + diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp new file mode 100644 index 000000000000..0a33d5adbf06 --- /dev/null +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -0,0 +1,424 @@ +//===-- SymbolContext.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/Symbol/SymbolContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolVendor.h" + +using namespace lldb; +using namespace lldb_private; + +SymbolContext::SymbolContext() : + target_sp (), + module_sp (), + comp_unit (NULL), + function (NULL), + block (NULL), + line_entry (), + symbol (NULL) +{ +} + +SymbolContext::SymbolContext(const ModuleSP& m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) : + target_sp (), + module_sp (m), + comp_unit (cu), + function (f), + block (b), + line_entry (), + symbol (s) +{ + if (le) + line_entry = *le; +} + +SymbolContext::SymbolContext(const TargetSP &t, const ModuleSP& m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) : + target_sp (t), + module_sp (m), + comp_unit (cu), + function (f), + block (b), + line_entry (), + symbol (s) +{ + if (le) + line_entry = *le; +} + +SymbolContext::SymbolContext(const SymbolContext& rhs) : + target_sp (rhs.target_sp), + module_sp (rhs.module_sp), + comp_unit (rhs.comp_unit), + function (rhs.function), + block (rhs.block), + line_entry (rhs.line_entry), + symbol (rhs.symbol) +{ +} + + +SymbolContext::SymbolContext (SymbolContextScope *sc_scope) : + target_sp (), + module_sp (), + comp_unit (NULL), + function (NULL), + block (NULL), + line_entry (), + symbol (NULL) +{ + sc_scope->CalculateSymbolContext (this); +} + +const SymbolContext& +SymbolContext::operator= (const SymbolContext& rhs) +{ + if (this != &rhs) + { + target_sp = rhs.target_sp; + module_sp = rhs.module_sp; + comp_unit = rhs.comp_unit; + function = rhs.function; + block = rhs.block; + line_entry = rhs.line_entry; + symbol = rhs.symbol; + } + return *this; +} + +void +SymbolContext::Clear() +{ + target_sp.reset(); + module_sp.reset(); + comp_unit = NULL; + function = NULL; + block = NULL; + line_entry.Clear(); + symbol = NULL; +} + +void +SymbolContext::DumpStopContext +( + Stream *s, + ExecutionContextScope *exe_scope, + const Address &addr, + bool show_module +) const +{ + Process *process = NULL; + if (exe_scope) + process = exe_scope->CalculateProcess(); + addr_t load_addr = addr.GetLoadAddress (process); + + if (show_module && module_sp) + { + *s << module_sp->GetFileSpec().GetFilename() << '`'; + } + + if (function != NULL) + { + if (function->GetMangled().GetName()) + function->GetMangled().GetName().Dump(s); + + const addr_t func_load_addr = function->GetAddressRange().GetBaseAddress().GetLoadAddress(process); + if (load_addr > func_load_addr) + s->Printf(" + %llu", load_addr - func_load_addr); + + if (block != NULL) + { + s->IndentMore(); + block->DumpStopContext(s, this); + s->IndentLess(); + } + else + { + if (line_entry.IsValid()) + { + s->PutCString(" at "); + if (line_entry.DumpStopContext(s)) + return; + } + } + } + else if (symbol != NULL) + { + symbol->GetMangled().GetName().Dump(s); + + if (symbol->GetAddressRangePtr()) + { + const addr_t sym_load_addr = symbol->GetAddressRangePtr()->GetBaseAddress().GetLoadAddress(process); + if (load_addr > sym_load_addr) + s->Printf(" + %llu", load_addr - sym_load_addr); + } + } + else + { + addr.Dump(s, exe_scope, Address::DumpStyleModuleWithFileAddress); + } +} + +void +SymbolContext::Dump(Stream *s, Process *process) const +{ + *s << (void *)this << ": "; + s->Indent(); + s->PutCString("SymbolContext"); + s->IndentMore(); + s->EOL(); + s->IndentMore(); + s->Indent(); + *s << "Module = " << (void *)module_sp.get() << ' '; + if (module_sp) + module_sp->GetFileSpec().Dump(s); + s->EOL(); + s->Indent(); + *s << "CompileUnit = " << (void *)comp_unit; + if (comp_unit != NULL) + *s << " {" << comp_unit->GetID() << "} " << *(dynamic_cast (comp_unit)); + s->EOL(); + s->Indent(); + *s << "Function = " << (void *)function; + if (function != NULL) + { + *s << " {" << function->GetID() << "} ";/// << function->GetType()->GetName(); +// Type* func_type = function->Type(); +// if (func_type) +// { +// s->EOL(); +// const UserDefType* func_udt = func_type->GetUserDefinedType().get(); +// if (func_udt) +// { +// s->IndentMore(); +// func_udt->Dump(s, func_type); +// s->IndentLess(); +// } +// } + } + s->EOL(); + s->Indent(); + *s << "Block = " << (void *)block; + if (block != NULL) + *s << " {" << block->GetID() << '}'; + // Dump the block and pass it a negative depth to we print all the parent blocks + //if (block != NULL) + // block->Dump(s, function->GetFileAddress(), INT_MIN); + s->EOL(); + s->Indent(); + *s << "LineEntry = "; + line_entry.Dump (s, process, true, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, true); + s->EOL(); + s->Indent(); + *s << "Symbol = " << (void *)symbol; + if (symbol != NULL && symbol->GetMangled()) + *s << ' ' << symbol->GetMangled().GetName().AsCString(); + s->EOL(); + s->IndentLess(); + s->IndentLess(); +} + +bool +lldb_private::operator== (const SymbolContext& lhs, const SymbolContext& rhs) +{ + return lhs.target_sp.get() == rhs.target_sp.get() && + lhs.module_sp.get() == rhs.module_sp.get() && + lhs.comp_unit == rhs.comp_unit && + lhs.function == rhs.function && + LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0 && + lhs.symbol == rhs.symbol; +} + +bool +lldb_private::operator!= (const SymbolContext& lhs, const SymbolContext& rhs) +{ + return lhs.target_sp.get() != rhs.target_sp.get() || + lhs.module_sp.get() != rhs.module_sp.get() || + lhs.comp_unit != rhs.comp_unit || + lhs.function != rhs.function || + LineEntry::Compare(lhs.line_entry, rhs.line_entry) != 0 || + lhs.symbol != rhs.symbol; +} + +bool +SymbolContext::GetAddressRange (uint32_t scope, AddressRange &range) const +{ + if ((scope & eSymbolContextLineEntry) && line_entry.IsValid()) + { + range = line_entry.range; + return true; + } + else if ((scope & eSymbolContextFunction) && function != NULL) + { + range = function->GetAddressRange(); + return true; + } + else if ((scope & eSymbolContextSymbol) && symbol != NULL && symbol->GetAddressRangePtr()) + { + range = *symbol->GetAddressRangePtr(); + + if (range.GetByteSize() == 0) + { + if (module_sp) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + { + Symtab *symtab = objfile->GetSymtab(); + if (symtab) + range.SetByteSize(symtab->CalculateSymbolSize (symbol)); + } + } + } + return true; + } + range.Clear(); + return false; +} + + +Function * +SymbolContext::FindFunctionByName (const char *name) const +{ + ConstString name_const_str (name); + if (function != NULL) + { + // FIXME: Look in the class of the current function, if it exists, + // for methods matching name. + } + + // + if (comp_unit != NULL) + { + // Make sure we've read in all the functions. We should be able to check and see + // if there's one by this name present before we do this... + module_sp->GetSymbolVendor()->ParseCompileUnitFunctions(*this); + uint32_t func_idx; + lldb::FunctionSP func_sp; + for (func_idx = 0; (func_sp = comp_unit->GetFunctionAtIndex(func_idx)) != NULL; ++func_idx) + { + if (func_sp->GetMangled().GetName() == name_const_str) + return func_sp.get(); + } + } + if (module_sp != NULL) + { + SymbolContextList sc_matches; + if (module_sp->FindFunctions (name_const_str, false, sc_matches) > 0) + { + SymbolContext sc; + sc_matches.GetContextAtIndex (0, sc); + return sc.function; + } + } + + if (target_sp) + { + SymbolContextList sc_matches; + if (target_sp->GetImages().FindFunctions (name_const_str, sc_matches) > 0) + { + SymbolContext sc; + sc_matches.GetContextAtIndex (0, sc); + return sc.function; + } + } + + return NULL; +} + +lldb::VariableSP +SymbolContext::FindVariableByName (const char *name) const +{ + lldb::VariableSP return_value; + return return_value; +} + +lldb::TypeSP +SymbolContext::FindTypeByName (const char *name) const +{ + lldb::TypeSP return_value; + return return_value; +} + +//---------------------------------------------------------------------- +// +// SymbolContextList +// +//---------------------------------------------------------------------- + + +SymbolContextList::SymbolContextList() : + m_symbol_contexts() +{ +} + +SymbolContextList::~SymbolContextList() +{ +} + +void +SymbolContextList::Append(const SymbolContext& sc) +{ + m_symbol_contexts.push_back(sc); +} + +void +SymbolContextList::Clear() +{ + m_symbol_contexts.clear(); +} + +void +SymbolContextList::Dump(Stream *s, Process *process) const +{ + + *s << (void *)this << ": "; + s->Indent(); + s->PutCString("SymbolContextList"); + s->EOL(); + s->IndentMore(); + + collection::const_iterator pos, end = m_symbol_contexts.end(); + for (pos = m_symbol_contexts.begin(); pos != end; ++pos) + { + pos->Dump(s, process); + } + s->IndentLess(); +} + +bool +SymbolContextList::GetContextAtIndex(uint32_t idx, SymbolContext& sc) const +{ + if (idx < m_symbol_contexts.size()) + { + sc = m_symbol_contexts[idx]; + return true; + } + return false; +} + +bool +SymbolContextList::RemoveContextAtIndex (uint32_t idx) +{ + if (idx < m_symbol_contexts.size()) + { + m_symbol_contexts.erase(m_symbol_contexts.begin() + idx); + return true; + } + return false; +} + +uint32_t +SymbolContextList::GetSize() const +{ + return m_symbol_contexts.size(); +} diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp new file mode 100644 index 000000000000..edfd56d3bd96 --- /dev/null +++ b/lldb/source/Symbol/SymbolFile.cpp @@ -0,0 +1,50 @@ +//===-- SymbolFile.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/Symbol/SymbolFile.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb_private; + +SymbolFile* +SymbolFile::FindPlugin (ObjectFile* obj_file) +{ + std::auto_ptr best_sym_file_ap; + if (obj_file != NULL) + { + // TODO: Load any plug-ins in the appropriate plug-in search paths and + // iterate over all of them to find the best one for the job. + + //---------------------------------------------------------------------- + // We currently only have one debug symbol parser... + //---------------------------------------------------------------------- + std::auto_ptr best_symfile_ap; + uint32_t best_symfile_abilities = 0; + + SymbolFileCreateInstance create_callback; + for (uint32_t idx = 0; (create_callback = PluginManager::GetSymbolFileCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::auto_ptr curr_symfile_ap(create_callback(obj_file)); + + if (curr_symfile_ap.get()) + { + uint32_t sym_file_abilities = curr_symfile_ap->GetAbilities(); + if (sym_file_abilities > best_symfile_abilities) + { + best_symfile_abilities = sym_file_abilities; + best_sym_file_ap = curr_symfile_ap; + } + } + } + } + return best_sym_file_ap.release(); +} + + diff --git a/lldb/source/Symbol/SymbolVendor.mm b/lldb/source/Symbol/SymbolVendor.mm new file mode 100644 index 000000000000..5fb8b0ab8d1b --- /dev/null +++ b/lldb/source/Symbol/SymbolVendor.mm @@ -0,0 +1,386 @@ +//===-- SymbolVendor.mm -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/SymbolVendor.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// FindPlugin +// +// Platforms can register a callback to use when creating symbol +// vendors to allow for complex debug information file setups, and to +// also allow for finding separate debug information files. +//---------------------------------------------------------------------- +SymbolVendor* +SymbolVendor::FindPlugin (Module* module) +{ + std::auto_ptr instance_ap; + //---------------------------------------------------------------------- + // We currently only have one debug symbol parser... + //---------------------------------------------------------------------- + SymbolVendorCreateInstance create_callback; + for (uint32_t idx = 0; (create_callback = PluginManager::GetSymbolVendorCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + instance_ap.reset(create_callback(module)); + + if (instance_ap.get()) + { + // TODO: make sure this symbol vendor is what we want. We + // currently are just returning the first one we find, but + // we may want to call this function only when we have our + // main executable module and then give all symbol vendor + // plug-ins a chance to compete for who wins. + return instance_ap.release(); + } + } + // The default implementation just tries to create debug information using the + // file representation for the module. + instance_ap.reset(new SymbolVendor(module)); + if (instance_ap.get()) + instance_ap->AddSymbolFileRepresendation(module->GetObjectFile()); + return instance_ap.release(); +} + +//---------------------------------------------------------------------- +// SymbolVendor constructor +//---------------------------------------------------------------------- +SymbolVendor::SymbolVendor(Module *module) : + ModuleChild(module), + m_mutex (Mutex::eMutexTypeRecursive), + m_type_list(), + m_compile_units(), + m_sym_file_ap() +{ + ObjectFile * objfile = module->GetObjectFile(); + ConstString target_triple; + if (objfile && objfile->GetTargetTriple(target_triple)) + { + m_type_list.GetClangASTContext().SetTargetTriple (target_triple.AsCString()); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendor::~SymbolVendor() +{ +} + +//---------------------------------------------------------------------- +// Add a represantion given an object file. +//---------------------------------------------------------------------- +void +SymbolVendor::AddSymbolFileRepresendation(ObjectFile *obj_file) +{ + Mutex::Locker locker(m_mutex); + if (obj_file != NULL) + m_sym_file_ap.reset(SymbolFile::FindPlugin(obj_file)); +} + +bool +SymbolVendor::SetCompileUnitAtIndex +(CompUnitSP& cu, uint32_t idx) +{ + Mutex::Locker locker(m_mutex); + const uint32_t num_compile_units = GetNumCompileUnits(); + if (idx < num_compile_units) + { + // Fire off an assertion if this compile unit already exists for now. + // The partial parsing should take care of only setting the compile + // unit once, so if this assertion fails, we need to make sure that + // we don't have a race condition, or have a second parse of the same + // compile unit. + assert(m_compile_units[idx].get() == NULL); + m_compile_units[idx] = cu; + return true; + } + return false; +} + +uint32_t +SymbolVendor::GetNumCompileUnits() +{ + Mutex::Locker locker(m_mutex); + if (m_compile_units.empty()) + { + if (m_sym_file_ap.get()) + { + // Resize our array of compile unit shared pointers -- which will + // each remain NULL until someone asks for the actual compile unit + // information. When this happens, the symbol file will be asked + // to parse this compile unit information. + m_compile_units.resize(m_sym_file_ap->GetNumCompileUnits()); + } + } + return m_compile_units.size(); +} + +size_t +SymbolVendor::ParseCompileUnitFunctions (const SymbolContext &sc) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitFunctions(sc); + return 0; +} + +bool +SymbolVendor::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitLineTable(sc); + return false; +} + +bool +SymbolVendor::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitSupportFiles(sc, support_files); + return false; +} + +size_t +SymbolVendor::ParseFunctionBlocks (const SymbolContext &sc) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseFunctionBlocks(sc); + return 0; +} + +size_t +SymbolVendor::ParseTypes (const SymbolContext &sc) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseTypes(sc); + return 0; +} + +size_t +SymbolVendor::ParseVariablesForContext (const SymbolContext& sc) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseVariablesForContext(sc); + return 0; +} + +Type* +SymbolVendor::ResolveTypeUID(lldb::user_id_t type_uid) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ResolveTypeUID(type_uid); + return NULL; +} + + +uint32_t +SymbolVendor::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ResolveSymbolContext(so_addr, resolve_scope, sc); + return 0; +} + +uint32_t +SymbolVendor::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); + return 0; +} + +uint32_t +SymbolVendor::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindGlobalVariables(name, append, max_matches, variables); + return 0; +} + +uint32_t +SymbolVendor::FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindGlobalVariables(regex, append, max_matches, variables); + return 0; +} + +uint32_t +SymbolVendor::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindFunctions(name, append, sc_list); + return 0; +} + +uint32_t +SymbolVendor::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Mutex::Locker locker(m_mutex); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindFunctions(regex, append, sc_list); + return 0; +} + + + +//uint32_t +//SymbolVendor::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& types) +//{ +// Mutex::Locker locker(m_mutex); +// if (m_sym_file_ap.get()) +// { +// lldb::user_id_t udt_uid = LLDB_INVALID_UID; +// if (encoding == Type::user_defined_type) +// udt_uid = UserDefType::GetUserDefTypeUID(udt_name); +// +// return m_sym_file_ap->FindTypes(sc, name, append, max_matches, encoding, udt_uid, types); +// } +// return 0; +//} +// +//uint32_t +//SymbolVendor::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& types) +//{ +// Mutex::Locker locker(m_mutex); +// if (m_sym_file_ap.get()) +// { +// lldb::user_id_t udt_uid = LLDB_INVALID_UID; +// +// if (encoding == Type::user_defined_type) +// udt_uid = UserDefType::GetUserDefTypeUID(udt_name); +// +// return m_sym_file_ap->FindTypes(sc, regex, append, max_matches, encoding, udt_uid, types); +// } +// return 0; +//} + +void +SymbolVendor::Dump(Stream *s) +{ + Mutex::Locker locker(m_mutex); + bool show_context = false; + + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->PutCString("SymbolVendor"); + if (m_sym_file_ap.get()) + { + ObjectFile *objfile = m_sym_file_ap->GetObjectFile(); + if (objfile) + { + const FileSpec &objfile_file_spec = objfile->GetFileSpec(); + if (objfile_file_spec) + { + s->PutCString(" ("); + objfile_file_spec.Dump(s); + s->PutChar(')'); + } + } + } + s->EOL(); + s->IndentMore(); + m_type_list.Dump(s, show_context); + + CompileUnitConstIter cu_pos, cu_end; + cu_end = m_compile_units.end(); + for (cu_pos = m_compile_units.begin(); cu_pos != cu_end; ++cu_pos) + { + // We currently only dump the compile units that have been parsed + if (cu_pos->get()) + (*cu_pos)->Dump(s, show_context); + } + + s->IndentLess(); + +} + +CompUnitSP +SymbolVendor::GetCompileUnitAtIndex(uint32_t idx) +{ + Mutex::Locker locker(m_mutex); + CompUnitSP cu_sp; + const uint32_t num_compile_units = GetNumCompileUnits(); + if (idx < num_compile_units) + { + cu_sp = m_compile_units[idx]; + if (cu_sp.get() == NULL) + { + m_compile_units[idx] = m_sym_file_ap->ParseCompileUnitAtIndex(idx); + cu_sp = m_compile_units[idx]; + } + } + return cu_sp; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolVendor::GetPluginName() +{ + return "SymbolVendor"; +} + +const char * +SymbolVendor::GetShortPluginName() +{ + return "vendor-default"; +} + +uint32_t +SymbolVendor::GetPluginVersion() +{ + return 1; +} + +void +SymbolVendor::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolVendor::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolVendor::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp new file mode 100644 index 000000000000..f1ed356d44b2 --- /dev/null +++ b/lldb/source/Symbol/Symtab.cpp @@ -0,0 +1,596 @@ +//===-- Symtab.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/Core/Module.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symtab.h" + +using namespace lldb; +using namespace lldb_private; + + + +Symtab::Symtab(ObjectFile *objfile) : + m_objfile(objfile), + m_symbols(), + m_addr_indexes(), + m_name_to_index() +{ +} + +Symtab::~Symtab() +{ +} + +void +Symtab::Reserve(uint32_t count) +{ + m_symbols.reserve (count); +} + +Symbol * +Symtab::Resize(uint32_t count) +{ + m_symbols.resize (count); + return &m_symbols[0]; +} + +uint32_t +Symtab::AddSymbol(const Symbol& symbol) +{ + uint32_t symbol_idx = m_symbols.size(); + m_name_to_index.Clear(); + m_addr_indexes.clear(); + m_symbols.push_back(symbol); + return symbol_idx; +} + +size_t +Symtab::GetNumSymbols() const +{ + return m_symbols.size(); +} + +void +Symtab::Dump(Stream *s, Process *process) const +{ + const_iterator pos; + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + const FileSpec &file_spec = m_objfile->GetFileSpec(); + const char * object_name = NULL; + if (m_objfile->GetModule()) + object_name = m_objfile->GetModule()->GetObjectName().GetCString(); + + if (file_spec) + s->Printf("Symtab, file = %s/%s%s%s%s, num_symbols = %u:\n", + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString(), + object_name ? "(" : "", + object_name ? object_name : "", + object_name ? ")" : "", + m_symbols.size()); + else + s->Printf("Symtab, num_symbols = %u:\n", m_symbols.size()); + s->IndentMore(); + + if (!m_symbols.empty()) + { + const_iterator begin = m_symbols.begin(); + const_iterator end = m_symbols.end(); + DumpSymbolHeader (s); + for (pos = m_symbols.begin(); pos != end; ++pos) + { + s->Indent(); + pos->Dump(s, process, std::distance(begin, pos)); + } + } + s->IndentLess (); +} + +void +Symtab::Dump(Stream *s, Process *process, std::vector& indexes) const +{ + const size_t num_symbols = GetNumSymbols(); + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->Printf("Symtab %u symbol indexes (%u symbols total):\n", indexes.size(), m_symbols.size()); + s->IndentMore(); + + if (!indexes.empty()) + { + std::vector::const_iterator pos; + std::vector::const_iterator end = indexes.end(); + DumpSymbolHeader (s); + for (pos = indexes.begin(); pos != end; ++pos) + { + uint32_t idx = *pos; + if (idx < num_symbols) + { + s->Indent(); + m_symbols[idx].Dump(s, process, idx); + } + } + } + s->IndentLess (); +} + +void +Symtab::DumpSymbolHeader (Stream *s) +{ + s->Indent(" Debug symbol\n"); + s->Indent(" |Synthetic symbol\n"); + s->Indent(" ||Externally Visible\n"); + s->Indent(" |||\n"); + s->Indent("Index UserID DSX Type File Address/Value Load Address Size Flags Name\n"); + s->Indent("------- ------ --- ------------ ------------------ ------------------ ------------------ ---------- ----------------------------------\n"); +} + +Symbol * +Symtab::SymbolAtIndex(uint32_t idx) +{ + if (idx < m_symbols.size()) + return &m_symbols[idx]; + return NULL; +} + + +const Symbol * +Symtab::SymbolAtIndex(uint32_t idx) const +{ + if (idx < m_symbols.size()) + return &m_symbols[idx]; + return NULL; +} + +//---------------------------------------------------------------------- +// InitNameIndexes +//---------------------------------------------------------------------- +void +Symtab::InitNameIndexes() +{ + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + // Create the name index vector to be able to quickly search by name + const size_t count = m_symbols.size(); + assert(m_objfile != NULL); + assert(m_objfile->GetModule() != NULL); + m_name_to_index.Reserve (count); + + UniqueCStringMap::Entry entry; + + for (entry.value = 0; entry.value < count; ++entry.value) + { + const Symbol *symbol = &m_symbols[entry.value]; + + // Don't let trampolines get into the lookup by name map + // If we ever need the trampoline symbols to be searchable by name + // we can remove this and then possibly add a new bool to any of the + // Symtab functions that lookup symbols by name to indicate if they + // want trampolines. + if (symbol->IsTrampoline()) + continue; + + const Mangled &mangled = symbol->GetMangled(); + entry.cstring = mangled.GetMangledName().GetCString(); + if (entry.cstring && entry.cstring[0]) + m_name_to_index.Append (entry); + + entry.cstring = mangled.GetDemangledName().GetCString(); + if (entry.cstring && entry.cstring[0]) + m_name_to_index.Append (entry); + } + m_name_to_index.Sort(); +} + +uint32_t +Symtab::AppendSymbolIndexesWithType(SymbolType symbol_type, std::vector& indexes, uint32_t start_idx, uint32_t end_index) const +{ + uint32_t prev_size = indexes.size(); + + const uint32_t count = std::min (m_symbols.size(), end_index); + + for (uint32_t i = start_idx; i < count; ++i) + { + if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) + indexes.push_back(i); + } + + return indexes.size() - prev_size; +} + +struct SymbolSortInfo +{ + const bool sort_by_load_addr; + const Symbol *symbols; +}; + +int +Symtab::CompareSymbolValueByIndex (void *thunk, const void *a, const void *b) +{ + const Symbol *symbols = (const Symbol *)thunk; + uint32_t index_a = *((uint32_t *) a); + uint32_t index_b = *((uint32_t *) b); + + addr_t value_a; + addr_t value_b; + if (symbols[index_a].GetValue().GetSection() == symbols[index_b].GetValue().GetSection()) + { + value_a = symbols[index_a].GetValue ().GetOffset(); + value_b = symbols[index_b].GetValue ().GetOffset(); + } + else + { + value_a = symbols[index_a].GetValue ().GetFileAddress(); + value_b = symbols[index_b].GetValue ().GetFileAddress(); + } + + if (value_a == value_b) + { + // The if the values are equal, use the original symbol user ID + lldb::user_id_t uid_a = symbols[index_a].GetID(); + lldb::user_id_t uid_b = symbols[index_b].GetID(); + if (uid_a < uid_b) + return -1; + if (uid_a > uid_b) + return 1; + return 0; + } + else if (value_a < value_b) + return -1; + + return 1; +} + +void +Symtab::SortSymbolIndexesByValue (std::vector& indexes, bool remove_duplicates) const +{ + // No need to sort if we have zero or one items... + if (indexes.size() <= 1) + return; + + // Sort the indexes in place using qsort + ::qsort_r (&indexes[0], indexes.size(), sizeof(uint32_t), (void *)&m_symbols[0], Symtab::CompareSymbolValueByIndex); + + // Remove any duplicates if requested + if (remove_duplicates) + std::unique(indexes.begin(), indexes.end()); +} + +uint32_t +Symtab::AppendSymbolIndexesWithName(const ConstString& symbol_name, std::vector& indexes) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + if (symbol_name) + { + const size_t old_size = indexes.size(); + if (m_name_to_index.IsEmpty()) + InitNameIndexes(); + + const char *symbol_cstr = symbol_name.GetCString(); + const UniqueCStringMap::Entry *entry_ptr; + for (entry_ptr = m_name_to_index.FindFirstValueForName (symbol_cstr); + entry_ptr!= NULL; + entry_ptr = m_name_to_index.FindNextValueForName (symbol_cstr, entry_ptr)) + { + indexes.push_back (entry_ptr->value); + } + return indexes.size() - old_size; + } + return 0; +} + +uint32_t +Symtab::AppendSymbolIndexesWithNameAndType(const ConstString& symbol_name, SymbolType symbol_type, std::vector& indexes) +{ + if (AppendSymbolIndexesWithName(symbol_name, indexes) > 0) + { + std::vector::iterator pos = indexes.begin(); + while (pos != indexes.end()) + { + if (symbol_type == eSymbolTypeAny || m_symbols[*pos].GetType() == symbol_type) + ++pos; + else + indexes.erase(pos); + } + } + return indexes.size(); +} + +uint32_t +Symtab::AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®exp, SymbolType symbol_type, std::vector& indexes) +{ + uint32_t prev_size = indexes.size(); + uint32_t sym_end = m_symbols.size(); + + for (int i = 0; i < sym_end; i++) + { + if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) + { + const char *name = m_symbols[i].GetMangled().GetName().AsCString(); + if (name) + { + if (regexp.Execute (name)) + indexes.push_back(i); + } + } + } + return indexes.size() - prev_size; + +} + +Symbol * +Symtab::FindSymbolWithType(SymbolType symbol_type, uint32_t& start_idx) +{ + const size_t count = m_symbols.size(); + for (uint32_t idx = start_idx; idx < count; ++idx) + { + if (symbol_type == eSymbolTypeAny || m_symbols[idx].GetType() == symbol_type) + { + start_idx = idx; + return &m_symbols[idx]; + } + } + return NULL; +} + +const Symbol * +Symtab::FindSymbolWithType(SymbolType symbol_type, uint32_t& start_idx) const +{ + const size_t count = m_symbols.size(); + for (uint32_t idx = start_idx; idx < count; ++idx) + { + if (symbol_type == eSymbolTypeAny || m_symbols[idx].GetType() == symbol_type) + { + start_idx = idx; + return &m_symbols[idx]; + } + } + return NULL; +} + +size_t +Symtab::FindAllSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, std::vector& symbol_indexes) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + // Initialize all of the lookup by name indexes before converting NAME + // to a uniqued string NAME_STR below. + if (m_name_to_index.IsEmpty()) + InitNameIndexes(); + + if (name) + { + // The string table did have a string that matched, but we need + // to check the symbols and match the symbol_type if any was given. + AppendSymbolIndexesWithNameAndType(name, symbol_type, symbol_indexes); + } + return symbol_indexes.size(); +} + +size_t +Symtab::FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, SymbolType symbol_type, std::vector& symbol_indexes) +{ + AppendSymbolIndexesMatchingRegExAndType(regex, symbol_type, symbol_indexes); + return symbol_indexes.size(); +} + +Symbol * +Symtab::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + if (m_name_to_index.IsEmpty()) + InitNameIndexes(); + + if (name) + { + std::vector matching_indexes; + // The string table did have a string that matched, but we need + // to check the symbols and match the symbol_type if any was given. + if (AppendSymbolIndexesWithNameAndType(name, symbol_type, matching_indexes)) + { + std::vector::const_iterator pos, end = matching_indexes.end(); + for (pos = matching_indexes.begin(); pos != end; ++pos) + { + Symbol *symbol = SymbolAtIndex(*pos); + + if (symbol->Compare(name, symbol_type)) + return symbol; + } + } + } + return NULL; +} + +typedef struct +{ + const Symtab *symtab; + const addr_t file_addr; + Symbol *match_symbol; + const uint32_t *match_index_ptr; + addr_t match_offset; +} SymbolSearchInfo; + +static int +SymbolWithFileAddress (SymbolSearchInfo *info, const uint32_t *index_ptr) +{ + const Symbol *curr_symbol = info->symtab->SymbolAtIndex (index_ptr[0]); + if (curr_symbol == NULL) + return -1; + + const addr_t info_file_addr = info->file_addr; + + // lldb::Symbol::GetAddressRangePtr() will only return a non NULL address + // range if the symbol has a section! + const AddressRange *curr_range = curr_symbol->GetAddressRangePtr(); + if (curr_range) + { + const addr_t curr_file_addr = curr_range->GetBaseAddress().GetFileAddress(); + if (info_file_addr < curr_file_addr) + return -1; + if (info_file_addr > curr_file_addr) + return +1; + info->match_symbol = const_cast(curr_symbol); + info->match_index_ptr = index_ptr; + return 0; + } + + return -1; +} + +static int +SymbolWithClosestFileAddress (SymbolSearchInfo *info, const uint32_t *index_ptr) +{ + const Symbol *symbol = info->symtab->SymbolAtIndex (index_ptr[0]); + if (symbol == NULL) + return -1; + + const addr_t info_file_addr = info->file_addr; + const AddressRange *curr_range = symbol->GetAddressRangePtr(); + if (curr_range) + { + const addr_t curr_file_addr = curr_range->GetBaseAddress().GetFileAddress(); + if (info_file_addr < curr_file_addr) + return -1; + + // Since we are finding the closest symbol that is greater than or equal + // to 'info->file_addr' we set the symbol here. This will get set + // multiple times, but after the search is done it will contain the best + // symbol match + info->match_symbol = const_cast(symbol); + info->match_index_ptr = index_ptr; + info->match_offset = info_file_addr - curr_file_addr; + + if (info_file_addr > curr_file_addr) + return +1; + return 0; + } + return -1; +} + +static SymbolSearchInfo +FindIndexPtrForSymbolContainingAddress(Symtab* symtab, addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes) +{ + SymbolSearchInfo info = { symtab, file_addr, NULL, NULL, 0 }; + bsearch(&info, indexes, num_indexes, sizeof(uint32_t), (comparison_function)SymbolWithClosestFileAddress); + return info; +} + + +void +Symtab::InitAddressIndexes() +{ + if (m_addr_indexes.empty()) + { + AppendSymbolIndexesWithType (eSymbolTypeFunction, m_addr_indexes); + AppendSymbolIndexesWithType (eSymbolTypeGlobal, m_addr_indexes); + AppendSymbolIndexesWithType (eSymbolTypeStatic, m_addr_indexes); + AppendSymbolIndexesWithType (eSymbolTypeCode, m_addr_indexes); + AppendSymbolIndexesWithType (eSymbolTypeTrampoline, m_addr_indexes); + AppendSymbolIndexesWithType (eSymbolTypeData, m_addr_indexes); + SortSymbolIndexesByValue(m_addr_indexes, true); + m_addr_indexes.push_back(UINT32_MAX); // Terminator for bsearch since we might need to look at the next symbol + } +} + +size_t +Symtab::CalculateSymbolSize (Symbol *symbol) +{ + // Make sure this symbol is from this symbol table... + if (symbol < m_symbols.data() && symbol >= m_symbols.data() + m_symbols.size()) + return 0; + + // See if this symbol already has a byte size? + size_t byte_size = symbol->GetByteSize(); + + if (byte_size) + { + // It does, just return it + return byte_size; + } + + // Else if this is an address based symbol, figure out the delta between + // it and the next address based symbol + if (symbol->GetAddressRangePtr()) + { + if (m_addr_indexes.empty()) + InitAddressIndexes(); + const size_t num_addr_indexes = m_addr_indexes.size(); + SymbolSearchInfo info = FindIndexPtrForSymbolContainingAddress(this, symbol->GetAddressRangePtr()->GetBaseAddress().GetFileAddress(), m_addr_indexes.data(), num_addr_indexes); + if (info.match_index_ptr != NULL) + { + const lldb::addr_t curr_file_addr = symbol->GetAddressRangePtr()->GetBaseAddress().GetFileAddress(); + // We can figure out the address range of all symbols except the + // last one by taking the delta between the current symbol and + // the next symbol + + for (uint32_t addr_index = info.match_index_ptr - m_addr_indexes.data() + 1; + addr_index < num_addr_indexes; + ++addr_index) + { + Symbol *next_symbol = SymbolAtIndex(m_addr_indexes[addr_index]); + if (next_symbol == NULL) + break; + + assert (next_symbol->GetAddressRangePtr()); + const lldb::addr_t next_file_addr = next_symbol->GetAddressRangePtr()->GetBaseAddress().GetFileAddress(); + if (next_file_addr > curr_file_addr) + { + byte_size = next_file_addr - curr_file_addr; + symbol->GetAddressRangePtr()->SetByteSize(byte_size); + symbol->SetSizeIsSynthesized(true); + break; + } + } + } + } + return byte_size; +} + +Symbol * +Symtab::FindSymbolWithFileAddress (addr_t file_addr) +{ + if (m_addr_indexes.empty()) + InitAddressIndexes(); + + SymbolSearchInfo info = { this, file_addr, NULL, NULL, 0 }; + + uint32_t* match = (uint32_t*)bsearch(&info, &m_addr_indexes[0], m_addr_indexes.size(), sizeof(uint32_t), (comparison_function)SymbolWithFileAddress); + if (match) + return SymbolAtIndex (*match); + return NULL; +} + + +Symbol * +Symtab::FindSymbolContainingFileAddress (addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes) +{ + SymbolSearchInfo info = { this, file_addr, NULL, NULL, 0 }; + + bsearch(&info, indexes, num_indexes, sizeof(uint32_t), (comparison_function)SymbolWithClosestFileAddress); + + if (info.match_symbol) + { + if (info.match_offset < CalculateSymbolSize(info.match_symbol)) + return info.match_symbol; + } + return NULL; +} + +Symbol * +Symtab::FindSymbolContainingFileAddress (addr_t file_addr) +{ + if (m_addr_indexes.empty()) + InitAddressIndexes(); + + return FindSymbolContainingFileAddress (file_addr, &m_addr_indexes[0], m_addr_indexes.size()); +} + diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp new file mode 100644 index 000000000000..9338ea2839af --- /dev/null +++ b/lldb/source/Symbol/Type.cpp @@ -0,0 +1,1531 @@ +//===-- Type.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Other libraries and framework includes +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/RecordLayout.h" + +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" + +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_ostream.h" + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContextScope.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +lldb_private::Type::Type +( + lldb::user_id_t uid, + SymbolFile* symbol_file, + const ConstString &name, + uint64_t byte_size, + SymbolContextScope *context, + lldb::user_id_t encoding_uid, + EncodingUIDType encoding_uid_type, + const Declaration& decl, + void *clang_type +) : + UserID (uid), + m_name (name), + m_byte_size (byte_size), + m_symbol_file (symbol_file), + m_context (context), + m_encoding_uid (encoding_uid), + m_encoding_uid_type (encoding_uid_type), + m_decl (decl), + m_clang_qual_type (clang_type) +{ +} + +lldb_private::Type::Type () : + UserID (0), + m_name (""), + m_byte_size (0), + m_symbol_file (NULL), + m_context (), + m_encoding_uid (0), + m_encoding_uid_type (eTypeInvalid), + m_decl (), + m_clang_qual_type (NULL) +{ +} + + +const lldb_private::Type& +lldb_private::Type::operator= (const Type& rhs) +{ + if (this != &rhs) + { + UserID::operator= (rhs); + m_name = rhs.m_name; + m_byte_size = rhs.m_byte_size; + m_symbol_file = rhs.m_symbol_file; + m_context = rhs.m_context; + m_encoding_uid = rhs.m_encoding_uid; + m_decl = rhs.m_decl; + m_clang_qual_type = rhs.m_clang_qual_type; + } + return *this; +} + + +void +lldb_private::Type::Dump (Stream *s, bool show_context) +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + *s << "Type" << (const UserID&)*this << ' '; + if (m_name) + *s << ", name = \"" << m_name << "\""; + + if (m_byte_size != 0) + s->Printf(", size = %zu", m_byte_size); + + if (show_context && m_context != NULL) + { + s->PutCString(", context = ( "); + m_context->DumpSymbolContext(s); + s->PutCString(" )"); + } + + m_decl.Dump(s); + + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(m_clang_qual_type)); + + if (qual_type.getTypePtr()) + { + *s << ", clang_type = "; + + clang::TagType *tag_type = dyn_cast(qual_type.getTypePtr()); + clang::TagDecl *tag_decl = NULL; + if (tag_type) + tag_decl = tag_type->getDecl(); + + if (tag_decl) + { + s->EOL(); + s->EOL(); + tag_decl->print(llvm::fouts(), 0); + s->EOL(); + } + else + { + const clang::TypedefType *typedef_type = qual_type->getAs(); + if (typedef_type) + { + const clang::TypedefDecl *typedef_decl = typedef_type->getDecl(); + std::string clang_typedef_name (typedef_decl->getQualifiedNameAsString()); + if (!clang_typedef_name.empty()) + *s << " (" << clang_typedef_name.c_str() << ')'; + } + else + { + // We have a clang type, lets show it + TypeList *type_list = GetTypeList(); + if (type_list) + { + clang::ASTContext *ast_context = GetClangAST(); + if (ast_context) + { + std::string clang_type_name(qual_type.getAsString()); + if (!clang_type_name.empty()) + *s << " (" << clang_type_name.c_str() << ')'; + } + } + } + } + } + else if (m_encoding_uid != LLDB_INVALID_UID) + { + *s << ", type_uid = " << m_encoding_uid; + switch (m_encoding_uid_type) + { + case eIsTypeWithUID: s->PutCString(" (unresolved type)"); break; + case eIsConstTypeWithUID: s->PutCString(" (unresolved const type)"); break; + case eIsRestrictTypeWithUID: s->PutCString(" (unresolved restrict type)"); break; + case eIsVolatileTypeWithUID: s->PutCString(" (unresolved volatile type)"); break; + case eTypedefToTypeWithUID: s->PutCString(" (unresolved typedef)"); break; + case ePointerToTypeWithUID: s->PutCString(" (unresolved pointer)"); break; + case eLValueReferenceToTypeWithUID: s->PutCString(" (unresolved L value reference)"); break; + case eRValueReferenceToTypeWithUID: s->PutCString(" (unresolved R value reference)"); break; + } + } + +// +// if (m_access) +// s->Printf(", access = %u", m_access); + s->EOL(); +} + +const lldb_private::ConstString & +lldb_private::Type::GetName() +{ + if (!(m_name)) + { + if (ResolveClangType()) + { + std::string type_name = ClangASTContext::GetTypeName (m_clang_qual_type); + if (!type_name.empty()) + m_name.SetCString (type_name.c_str()); + } + } + return m_name; +} + +int +lldb_private::Type::DumpClangTypeName(Stream *s, void *clang_type) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + std::string type_name; + const clang::TypedefType *typedef_type = qual_type->getAs(); + if (typedef_type) + { + const clang::TypedefDecl *typedef_decl = typedef_type->getDecl(); + type_name = typedef_decl->getQualifiedNameAsString(); + } + else + { + type_name = qual_type.getAsString(); + } + if (!type_name.empty()) + return s->Printf("(%s) ", type_name.c_str()); + return 0; +} + +lldb_private::ConstString +lldb_private::Type::GetClangTypeName (void *clang_type) +{ + ConstString clang_type_name; + if (clang_type) + { + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + + const clang::TypedefType *typedef_type = qual_type->getAs(); + if (typedef_type) + { + const clang::TypedefDecl *typedef_decl = typedef_type->getDecl(); + std::string clang_typedef_name (typedef_decl->getQualifiedNameAsString()); + if (!clang_typedef_name.empty()) + clang_type_name.SetCString (clang_typedef_name.c_str()); + } + else + { + std::string type_name(qual_type.getAsString()); + if (!type_name.empty()) + clang_type_name.SetCString (type_name.c_str()); + } + } + else + { + clang_type_name.SetCString (""); + } + + return clang_type_name; +} + + + +void +lldb_private::Type::DumpTypeName(Stream *s) +{ + GetName().Dump(s, ""); +} + + +void +lldb_private::Type::DumpValue +( + lldb_private::ExecutionContext *exe_ctx, + lldb_private::Stream *s, + const lldb_private::DataExtractor &data, + uint32_t data_byte_offset, + bool show_types, + bool show_summary, + bool verbose, + lldb::Format format +) +{ + if (ResolveClangType()) + { + if (show_types) + { + s->PutChar('('); + if (verbose) + s->Printf("Type{0x%8.8x} ", GetID()); + DumpTypeName (s); + s->PutCString(") "); + } + + lldb_private::Type::DumpValue (exe_ctx, + GetClangAST (), + m_clang_qual_type, + s, + format == lldb::eFormatDefault ? GetFormat() : format, + data, + data_byte_offset, + GetByteSize(), + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, + show_summary, + verbose, + 0); + } +} + + +void +lldb_private::Type::DumpSummary +( + ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_type, + Stream *s, + const lldb_private::DataExtractor &data, + uint32_t data_byte_offset, + size_t data_byte_size +) +{ + uint32_t length = 0; + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + if (ClangASTContext::IsCStringType (clang_type, length)) + { + + if (exe_ctx && exe_ctx->process) + { + uint32_t offset = data_byte_offset; + lldb::addr_t pointer_addresss = data.GetMaxU64(&offset, data_byte_size); + const size_t k_max_buf_size = length ? length : 256; + uint8_t buf[k_max_buf_size + 1]; + lldb_private::DataExtractor data(buf, k_max_buf_size, exe_ctx->process->GetByteOrder(), 4); + buf[k_max_buf_size] = '\0'; + size_t bytes_read; + size_t total_cstr_len = 0; + Error error; + while ((bytes_read = exe_ctx->process->ReadMemory (pointer_addresss, buf, k_max_buf_size, error)) > 0) + { + const size_t len = strlen((const char *)buf); + if (len == 0) + break; + if (total_cstr_len == 0) + s->PutCString (" \""); + data.Dump(s, 0, lldb::eFormatChar, 1, len, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + total_cstr_len += len; + if (len < k_max_buf_size) + break; + pointer_addresss += total_cstr_len; + } + if (total_cstr_len > 0) + s->PutChar ('"'); + } + } +} + +#define DEPTH_INCREMENT 2 +void +lldb_private::Type::DumpValue +( + ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_type, + Stream *s, + lldb::Format format, + const lldb_private::DataExtractor &data, + uint32_t data_byte_offset, + size_t data_byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool show_types, + bool show_summary, + bool verbose, + uint32_t depth +) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + switch (qual_type->getTypeClass()) + { + case clang::Type::Record: + { + const clang::RecordType *record_type = cast(qual_type.getTypePtr()); + const clang::RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + uint32_t field_bit_offset = 0; + uint32_t field_byte_offset = 0; + const clang::ASTRecordLayout &record_layout = ast_context->getASTRecordLayout(record_decl); + uint32_t child_idx = 0; + + + const clang::CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); + if (cxx_record_decl) + { + // We might have base classes to print out first + clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const clang::CXXRecordDecl *base_class_decl = cast(base_class->getType()->getAs()->getDecl()); + + // Skip empty base classes + if (verbose == false && ClangASTContext::RecordHasFields(base_class_decl) == false) + continue; + + if (base_class->isVirtual()) + field_bit_offset = record_layout.getVBaseClassOffset(base_class_decl); + else + field_bit_offset = record_layout.getBaseClassOffset(base_class_decl); + field_byte_offset = field_bit_offset / 8; + assert (field_bit_offset % 8 == 0); + if (child_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + clang::QualType base_class_qual_type = base_class->getType(); + std::string base_class_type_name(base_class_qual_type.getAsString()); + + // Indent and print the base class type name + s->Printf("\n%*s%s ", depth + DEPTH_INCREMENT, "", base_class_type_name.c_str()); + + std::pair base_class_type_info = ast_context->getTypeInfo(base_class_qual_type); + + // Dump the value of the member + Type::DumpValue ( + exe_ctx, + ast_context, // The clang AST context for this type + base_class_qual_type.getAsOpaquePtr(),// The clang type we want to dump + s, // Stream to dump to + Type::GetFormat(base_class_qual_type.getAsOpaquePtr()), // The format with which to display the member + data, // Data buffer containing all bytes for this type + data_byte_offset + field_byte_offset,// Offset into "data" where to grab value from + base_class_type_info.first / 8, // Size of this type in bytes + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + + ++child_idx; + } + } + const unsigned num_fields = record_layout.getFieldCount(); + + uint32_t field_idx = 0; + clang::RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) + { + // Print the starting squiggly bracket (if this is the + // first member) or comman (for member 2 and beyong) for + // the struct/union/class member. + if (child_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + // Indent + s->Printf("\n%*s", depth + DEPTH_INCREMENT, ""); + + clang::QualType field_type = field->getType(); + // Print the member type if requested + // Figure out the type byte size (field_type_info.first) and + // alignment (field_type_info.second) from the AST context. + std::pair field_type_info = ast_context->getTypeInfo(field_type); + assert(field_idx < num_fields); + // Figure out the field offset within the current struct/union/class type + field_bit_offset = record_layout.getFieldOffset (field_idx); + field_byte_offset = field_bit_offset / 8; + uint32_t field_bitfield_bit_size = 0; + uint32_t field_bitfield_bit_offset = 0; + if (ClangASTContext::FieldIsBitfield (ast_context, *field, field_bitfield_bit_size)) + field_bitfield_bit_offset = field_bit_offset % 8; + + if (show_types) + { + std::string field_type_name(field_type.getAsString()); + if (field_bitfield_bit_size > 0) + s->Printf("(%s:%u) ", field_type_name.c_str(), field_bitfield_bit_size); + else + s->Printf("(%s) ", field_type_name.c_str()); + } + // Print the member name and equal sign + s->Printf("%s = ", field->getNameAsString().c_str()); + + + // Dump the value of the member + Type::DumpValue ( + exe_ctx, + ast_context, // The clang AST context for this type + field_type.getAsOpaquePtr(), // The clang type we want to dump + s, // Stream to dump to + Type::GetFormat(field_type.getAsOpaquePtr()), // The format with which to display the member + data, // Data buffer containing all bytes for this type + data_byte_offset + field_byte_offset,// Offset into "data" where to grab value from + field_type_info.first / 8, // Size of this type in bytes + field_bitfield_bit_size, // Bitfield bit size + field_bitfield_bit_offset, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + } + + // Indent the trailing squiggly bracket + if (child_idx > 0) + s->Printf("\n%*s}", depth, ""); + } + return; + + case clang::Type::Enum: + { + const clang::EnumType *enum_type = cast(qual_type.getTypePtr()); + const clang::EnumDecl *enum_decl = enum_type->getDecl(); + assert(enum_decl); + clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; + uint32_t offset = data_byte_offset; + const int64_t enum_value = data.GetMaxU64Bitfield(&offset, data_byte_size, bitfield_bit_size, bitfield_bit_offset); + for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) + { + if (enum_pos->getInitVal() == enum_value) + { + s->Printf("%s", enum_pos->getNameAsCString()); + return; + } + } + // If we have gotten here we didn't get find the enumerator in the + // enum decl, so just print the integer. + s->Printf("%lli", enum_value); + } + return; + + case clang::Type::ConstantArray: + { + const clang::ConstantArrayType *array = cast(qual_type.getTypePtr()); + bool is_array_of_characters = false; + clang::QualType element_qual_type = array->getElementType(); + + clang::Type *canonical_type = element_qual_type->getCanonicalTypeInternal().getTypePtr(); + if (canonical_type) + is_array_of_characters = canonical_type->isCharType(); + + const uint64_t element_count = array->getSize().getLimitedValue(); + + std::pair field_type_info = ast_context->getTypeInfo(element_qual_type); + + uint32_t element_idx = 0; + uint32_t element_offset = 0; + uint64_t element_byte_size = field_type_info.first / 8; + uint32_t element_stride = element_byte_size; + + if (is_array_of_characters) + { + s->PutChar('"'); + data.Dump(s, data_byte_offset, lldb::eFormatChar, element_byte_size, element_count, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('"'); + return; + } + else + { + lldb::Format element_format = Type::GetFormat(element_qual_type.getAsOpaquePtr()); + + for (element_idx = 0; element_idx < element_count; ++element_idx) + { + // Print the starting squiggly bracket (if this is the + // first member) or comman (for member 2 and beyong) for + // the struct/union/class member. + if (element_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + // Indent and print the index + s->Printf("\n%*s[%u] ", depth + DEPTH_INCREMENT, "", element_idx); + + // Figure out the field offset within the current struct/union/class type + element_offset = element_idx * element_stride; + + // Dump the value of the member + Type::DumpValue ( + exe_ctx, + ast_context, // The clang AST context for this type + element_qual_type.getAsOpaquePtr(), // The clang type we want to dump + s, // Stream to dump to + element_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset + element_offset,// Offset into "data" where to grab value from + element_byte_size, // Size of this type in bytes + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + } + + // Indent the trailing squiggly bracket + if (element_idx > 0) + s->Printf("\n%*s}", depth, ""); + } + } + return; + + case clang::Type::Typedef: + { + clang::QualType typedef_qual_type = cast(qual_type)->LookThroughTypedefs(); + lldb::Format typedef_format = lldb_private::Type::GetFormat(typedef_qual_type.getAsOpaquePtr()); + std::pair typedef_type_info = ast_context->getTypeInfo(typedef_qual_type); + uint64_t typedef_byte_size = typedef_type_info.first / 8; + + return Type::DumpValue( + exe_ctx, + ast_context, // The clang AST context for this type + typedef_qual_type.getAsOpaquePtr(), // The clang type we want to dump + s, // Stream to dump to + typedef_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset, // Offset into "data" where to grab value from + typedef_byte_size, // Size of this type in bytes + bitfield_bit_size, // Bitfield bit size + bitfield_bit_offset,// Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth); // Scope depth for any types that have children + } + break; + + default: + // We are down the a scalar type that we just need to display. + data.Dump(s, data_byte_offset, format, data_byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset); + + if (show_summary) + Type::DumpSummary (exe_ctx, ast_context, clang_type, s, data, data_byte_offset, data_byte_size); + break; + } +} + +bool +lldb_private::Type::DumpTypeValue +( + Stream *s, + clang::ASTContext *ast_context, + void *clang_type, + lldb::Format format, + const lldb_private::DataExtractor &data, + uint32_t byte_offset, + size_t byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset +) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + if (ClangASTContext::IsAggregateType (clang_type)) + { + return 0; + } + else + { + switch (qual_type->getTypeClass()) + { + case clang::Type::Enum: + { + const clang::EnumType *enum_type = cast(qual_type.getTypePtr()); + const clang::EnumDecl *enum_decl = enum_type->getDecl(); + assert(enum_decl); + clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; + uint32_t offset = byte_offset; + const int64_t enum_value = data.GetMaxU64Bitfield (&offset, byte_size, bitfield_bit_size, bitfield_bit_offset); + for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) + { + if (enum_pos->getInitVal() == enum_value) + { + s->PutCString (enum_pos->getNameAsCString()); + return true; + } + } + // If we have gotten here we didn't get find the enumerator in the + // enum decl, so just print the integer. + + s->Printf("%lli", enum_value); + return true; + } + break; + + case clang::Type::Typedef: + { + clang::QualType typedef_qual_type = cast(qual_type)->LookThroughTypedefs(); + lldb::Format typedef_format = Type::GetFormat(typedef_qual_type.getAsOpaquePtr()); + std::pair typedef_type_info = ast_context->getTypeInfo(typedef_qual_type); + uint64_t typedef_byte_size = typedef_type_info.first / 8; + + return Type::DumpTypeValue( + s, + ast_context, // The clang AST context for this type + typedef_qual_type.getAsOpaquePtr(), // The clang type we want to dump + typedef_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + byte_offset, // Offset into "data" where to grab value from + typedef_byte_size, // Size of this type in bytes + bitfield_bit_size, // Size in bits of a bitfield value, if zero don't treat as a bitfield + bitfield_bit_offset); // Offset in bits of a bitfield value if bitfield_bit_size != 0 + } + break; + + default: + // We are down the a scalar type that we just need to display. + return data.Dump(s, + byte_offset, + format, + byte_size, + 1, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + bitfield_bit_size, + bitfield_bit_offset); + break; + } + } + return 0; +} + +bool +lldb_private::Type::GetValueAsScalar +( + clang::ASTContext *ast_context, + void *clang_type, + const lldb_private::DataExtractor &data, + uint32_t data_byte_offset, + size_t data_byte_size, + Scalar &value +) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + + if (ClangASTContext::IsAggregateType (clang_type)) + { + return false; // Aggregate types don't have scalar values + } + else + { + uint32_t count = 0; + lldb::Encoding encoding = Type::GetEncoding (clang_type, count); + + if (encoding == lldb::eEncodingInvalid || count != 1) + return false; + + uint64_t bit_width = ast_context->getTypeSize(qual_type); + uint32_t byte_size = (bit_width + 7 ) / 8; + uint32_t offset = data_byte_offset; + switch (encoding) + { + case lldb::eEncodingUint: + if (byte_size <= sizeof(unsigned long long)) + { + uint64_t uval64 = data.GetMaxU64 (&offset, byte_size); + if (byte_size <= sizeof(unsigned int)) + { + value = (unsigned int)uval64; + return true; + } + else if (byte_size <= sizeof(unsigned long)) + { + value = (unsigned long)uval64; + return true; + } + else if (byte_size <= sizeof(unsigned long long)) + { + value = (unsigned long long )uval64; + return true; + } + else + value.Clear(); + } + break; + + case lldb::eEncodingSint: + if (byte_size <= sizeof(long long)) + { + int64_t sval64 = (int64_t)data.GetMaxU64 (&offset, byte_size); + if (byte_size <= sizeof(int)) + { + value = (int)sval64; + return true; + } + else if (byte_size <= sizeof(long)) + { + value = (long)sval64; + return true; + } + else if (byte_size <= sizeof(long long)) + { + value = (long long )sval64; + return true; + } + else + value.Clear(); + } + break; + + case lldb::eEncodingIEEE754: + if (byte_size <= sizeof(long double)) + { + uint32_t u32; + uint64_t u64; + if (byte_size == sizeof(float)) + { + if (sizeof(float) == sizeof(uint32_t)) + { + u32 = data.GetU32(&offset); + value = *((float *)&u32); + return true; + } + else if (sizeof(float) == sizeof(uint64_t)) + { + u64 = data.GetU64(&offset); + value = *((float *)&u64); + return true; + } + } + else + if (byte_size == sizeof(double)) + { + if (sizeof(double) == sizeof(uint32_t)) + { + u32 = data.GetU32(&offset); + value = *((double *)&u32); + return true; + } + else if (sizeof(double) == sizeof(uint64_t)) + { + u64 = data.GetU64(&offset); + value = *((double *)&u64); + return true; + } + } + else + if (byte_size == sizeof(long double)) + { + if (sizeof(long double) == sizeof(uint32_t)) + { + u32 = data.GetU32(&offset); + value = *((long double *)&u32); + return true; + } + else if (sizeof(long double) == sizeof(uint64_t)) + { + u64 = data.GetU64(&offset); + value = *((long double *)&u64); + return true; + } + } + } + break; + } + } + return false; +} + +bool +lldb_private::Type::SetValueFromScalar +( + clang::ASTContext *ast_context, + void *clang_type, + const Scalar &value, + Stream &strm +) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + + // Aggregate types don't have scalar values + if (!ClangASTContext::IsAggregateType (clang_type)) + { + strm.GetFlags().Set(Stream::eBinary); + uint32_t count = 0; + lldb::Encoding encoding = Type::GetEncoding (clang_type, count); + + if (encoding == lldb::eEncodingInvalid || count != 1) + return false; + + uint64_t bit_width = ast_context->getTypeSize(qual_type); + // This function doesn't currently handle non-byte aligned assignments + if ((bit_width % 8) != 0) + return false; + + uint32_t byte_size = (bit_width + 7 ) / 8; + switch (encoding) + { + case lldb::eEncodingUint: + switch (byte_size) + { + case 1: strm.PutHex8(value.UInt()); return true; + case 2: strm.PutHex16(value.UInt()); return true; + case 4: strm.PutHex32(value.UInt()); return true; + case 8: strm.PutHex64(value.ULongLong()); return true; + default: + break; + } + break; + + case lldb::eEncodingSint: + switch (byte_size) + { + case 1: strm.PutHex8(value.SInt()); return true; + case 2: strm.PutHex16(value.SInt()); return true; + case 4: strm.PutHex32(value.SInt()); return true; + case 8: strm.PutHex64(value.SLongLong()); return true; + default: + break; + } + break; + + case lldb::eEncodingIEEE754: + if (byte_size <= sizeof(long double)) + { + if (byte_size == sizeof(float)) + { + strm.PutFloat(value.Float()); + return true; + } + else + if (byte_size == sizeof(double)) + { + strm.PutDouble(value.Double()); + return true; + } + else + if (byte_size == sizeof(long double)) + { + strm.PutDouble(value.LongDouble()); + return true; + } + } + break; + } + } + return false; +} + + +uint64_t +lldb_private::Type::GetByteSize() +{ + if (m_byte_size == 0) + { + switch (m_encoding_uid_type) + { + case eIsTypeWithUID: + case eIsConstTypeWithUID: + case eIsRestrictTypeWithUID: + case eIsVolatileTypeWithUID: + case eTypedefToTypeWithUID: + if (m_encoding_uid != LLDB_INVALID_UID) + { + Type *encoding_type = m_symbol_file->ResolveTypeUID (m_encoding_uid); + if (encoding_type) + m_byte_size = encoding_type->GetByteSize(); + } + if (m_byte_size == 0) + { + uint64_t bit_width = GetClangAST()->getTypeSize(clang::QualType::getFromOpaquePtr(GetOpaqueClangQualType())); + m_byte_size = (bit_width + 7 ) / 8; + } + break; + + // If we are a pointer or reference, then this is just a pointer size; + case ePointerToTypeWithUID: + case eLValueReferenceToTypeWithUID: + case eRValueReferenceToTypeWithUID: + m_byte_size = GetTypeList()->GetClangASTContext().GetPointerBitSize() / 8; + break; + } + } + return m_byte_size; +} + + +uint32_t +lldb_private::Type::GetNumChildren (bool omit_empty_base_classes) +{ + if (!ResolveClangType()) + return 0; + return ClangASTContext::GetNumChildren (m_clang_qual_type, omit_empty_base_classes); + +} + +bool +lldb_private::Type::IsAggregateType () +{ + if (ResolveClangType()) + return ClangASTContext::IsAggregateType (m_clang_qual_type); + return false; +} + +lldb::Format +lldb_private::Type::GetFormat () +{ + // Make sure we resolve our type if it already hasn't been. + if (!ResolveClangType()) + return lldb::eFormatInvalid; + return lldb_private::Type::GetFormat (m_clang_qual_type); +} + + +lldb::Format +lldb_private::Type::GetFormat (void *clang_type) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + + switch (qual_type->getTypeClass()) + { + case clang::Type::FunctionNoProto: + case clang::Type::FunctionProto: + break; + + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + break; + + case clang::Type::ConstantArray: + break; + + case clang::Type::ExtVector: + case clang::Type::Vector: + break; + + case clang::Type::Builtin: + switch (cast(qual_type)->getKind()) + { + default: assert(0 && "Unknown builtin type!"); + case clang::BuiltinType::Void: + break; + + case clang::BuiltinType::Bool: return lldb::eFormatBoolean; + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::WChar: return lldb::eFormatChar; + case clang::BuiltinType::Char16: return lldb::eFormatUnicode16; + case clang::BuiltinType::Char32: return lldb::eFormatUnicode32; + case clang::BuiltinType::UShort: return lldb::eFormatHex; + case clang::BuiltinType::Short: return lldb::eFormatDecimal; + case clang::BuiltinType::UInt: return lldb::eFormatHex; + case clang::BuiltinType::Int: return lldb::eFormatDecimal; + case clang::BuiltinType::ULong: return lldb::eFormatHex; + case clang::BuiltinType::Long: return lldb::eFormatDecimal; + case clang::BuiltinType::ULongLong: return lldb::eFormatHex; + case clang::BuiltinType::LongLong: return lldb::eFormatDecimal; + case clang::BuiltinType::UInt128: return lldb::eFormatHex; + case clang::BuiltinType::Int128: return lldb::eFormatDecimal; + case clang::BuiltinType::Float: return lldb::eFormatFloat; + case clang::BuiltinType::Double: return lldb::eFormatFloat; + case clang::BuiltinType::LongDouble: return lldb::eFormatFloat; + case clang::BuiltinType::NullPtr: return lldb::eFormatHex; + } + break; + case clang::Type::ObjCObjectPointer: return lldb::eFormatHex; + case clang::Type::BlockPointer: return lldb::eFormatHex; + case clang::Type::Pointer: return lldb::eFormatHex; + case clang::Type::LValueReference: + case clang::Type::RValueReference: return lldb::eFormatHex; + case clang::Type::MemberPointer: break; + case clang::Type::Complex: return lldb::eFormatComplex; + case clang::Type::ObjCInterface: break; + case clang::Type::Record: break; + case clang::Type::Enum: return lldb::eFormatEnum; + case clang::Type::Typedef: + return lldb_private::Type::GetFormat(cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr()); + + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: +// case clang::Type::QualifiedName: + case clang::Type::TemplateSpecialization: break; + } + // We don't know hot to display this type... + return lldb::eFormatBytes; +} + + +lldb::Encoding +lldb_private::Type::GetEncoding (uint32_t &count) +{ + // Make sure we resolve our type if it already hasn't been. + if (!ResolveClangType()) + return lldb::eEncodingInvalid; + + return Type::GetEncoding (m_clang_qual_type, count); +} + + +lldb::Encoding +lldb_private::Type::GetEncoding (void *clang_type, uint32_t &count) +{ + count = 1; + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + + switch (qual_type->getTypeClass()) + { + case clang::Type::FunctionNoProto: + case clang::Type::FunctionProto: + break; + + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + break; + + case clang::Type::ConstantArray: + break; + + case clang::Type::ExtVector: + case clang::Type::Vector: + // TODO: Set this to more than one??? + break; + + case clang::Type::Builtin: + switch (cast(qual_type)->getKind()) + { + default: assert(0 && "Unknown builtin type!"); + case clang::BuiltinType::Void: + break; + + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::WChar: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: return lldb::eEncodingSint; + + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: return lldb::eEncodingUint; + + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: return lldb::eEncodingIEEE754; + + case clang::BuiltinType::NullPtr: return lldb::eEncodingUint; + } + break; + // All pointer types are represented as unsigned integer encodings. + // We may nee to add a eEncodingPointer if we ever need to know the + // difference + case clang::Type::ObjCObjectPointer: + case clang::Type::BlockPointer: + case clang::Type::Pointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: return lldb::eEncodingUint; + // Complex numbers are made up of floats + case clang::Type::Complex: + count = 2; + return lldb::eEncodingIEEE754; + + case clang::Type::ObjCInterface: break; + case clang::Type::Record: break; + case clang::Type::Enum: return lldb::eEncodingSint; + case clang::Type::Typedef: + return Type::GetEncoding(cast(qual_type)->LookThroughTypedefs().getAsOpaquePtr(), count); + break; + + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: +// case clang::Type::QualifiedName: + case clang::Type::TemplateSpecialization: break; + } + count = 0; + return lldb::eEncodingInvalid; +} + + +bool +lldb_private::Type::DumpValueInMemory +( + lldb_private::ExecutionContext *exe_ctx, + lldb_private::Stream *s, + lldb::addr_t address, + lldb::AddressType address_type, + bool show_types, + bool show_summary, + bool verbose +) +{ + if (address != LLDB_INVALID_ADDRESS) + { + lldb_private::DataExtractor data; + data.SetByteOrder (exe_ctx->process->GetByteOrder()); + if (ReadFromMemory (exe_ctx, address, address_type, data)) + { + DumpValue(exe_ctx, s, data, 0, show_types, show_summary, verbose); + return true; + } + } + return false; +} + +bool +lldb_private::Type::ReadFromMemory +( + lldb_private::ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_type, + lldb::addr_t addr, + lldb::AddressType address_type, + lldb_private::DataExtractor &data +) +{ + if (address_type == lldb::eAddressTypeFile) + { + // Can't convert a file address to anything valid without more + // context (which Module it came from) + return false; + } + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + + const uint32_t byte_size = (ast_context->getTypeSize (qual_type) + 7) / 8; + if (data.GetByteSize() < byte_size) + { + lldb::DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = (uint8_t*)data.PeekData(0, byte_size); + if (dst != NULL) + { + if (address_type == lldb::eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy (dst, (uint8_t*)NULL + addr, byte_size); + return true; + } + else + { + if (exe_ctx && exe_ctx->process) + { + Error error; + return exe_ctx->process->ReadMemory(addr, dst, byte_size, error) == byte_size; + } + } + } + return false; +} + +bool +lldb_private::Type::WriteToMemory +( + lldb_private::ExecutionContext *exe_ctx, + clang::ASTContext *ast_context, + void *clang_type, + lldb::addr_t addr, + lldb::AddressType address_type, + StreamString &new_value +) +{ + if (address_type == lldb::eAddressTypeFile) + { + // Can't convert a file address to anything valid without more + // context (which Module it came from) + return false; + } + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); + const uint32_t byte_size = (ast_context->getTypeSize (qual_type) + 7) / 8; + + if (byte_size > 0) + { + if (address_type == lldb::eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy ((void *)addr, new_value.GetData(), byte_size); + return true; + } + else + { + if (exe_ctx && exe_ctx->process) + { + Error error; + return exe_ctx->process->WriteMemory(addr, new_value.GetData(), byte_size, error) == byte_size; + } + } + } + return false; +} + + +bool +lldb_private::Type::ReadFromMemory (lldb_private::ExecutionContext *exe_ctx, lldb::addr_t addr, lldb::AddressType address_type, lldb_private::DataExtractor &data) +{ + if (address_type == lldb::eAddressTypeFile) + { + // Can't convert a file address to anything valid without more + // context (which Module it came from) + return false; + } + + const uint32_t byte_size = GetByteSize(); + if (data.GetByteSize() < byte_size) + { + lldb::DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = (uint8_t*)data.PeekData(0, byte_size); + if (dst != NULL) + { + if (address_type == lldb::eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy (dst, (uint8_t*)NULL + addr, byte_size); + return true; + } + else + { + if (exe_ctx && exe_ctx->process) + { + Error error; + return exe_ctx->process->ReadMemory(addr, dst, byte_size, error) == byte_size; + } + } + } + return false; +} + + +bool +lldb_private::Type::WriteToMemory (lldb_private::ExecutionContext *exe_ctx, lldb::addr_t addr, lldb::AddressType address_type, lldb_private::DataExtractor &data) +{ + return false; +} + + +lldb_private::TypeList* +lldb_private::Type::GetTypeList() +{ + return GetSymbolFile()->GetObjectFile()->GetModule()->GetTypeList(); +} + + +bool +lldb_private::Type::ResolveClangType() +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(m_clang_qual_type)); + if (qual_type.getTypePtr() == NULL) + { + clang::QualType resolved_qual_type; + TypeList *type_list = GetTypeList(); + if (m_encoding_uid != LLDB_INVALID_UID) + { + Type *encoding_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); + if (encoding_type) + { + + switch (m_encoding_uid_type) + { + case eIsTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(encoding_type->GetOpaqueClangQualType()); + break; + + case eIsConstTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(ClangASTContext::AddConstModifier (encoding_type->GetOpaqueClangQualType())); + break; + + case eIsRestrictTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(ClangASTContext::AddRestrictModifier (encoding_type->GetOpaqueClangQualType())); + break; + + case eIsVolatileTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(ClangASTContext::AddVolatileModifier (encoding_type->GetOpaqueClangQualType())); + break; + + case eTypedefToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->CreateClangTypedefType (this, encoding_type)); + // Clear the name so it can get fully qualified in case the + // typedef is in a namespace. + m_name.Clear(); + break; + + case ePointerToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->CreateClangPointerType (encoding_type)); + break; + + case eLValueReferenceToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->CreateClangLValueReferenceType (encoding_type)); + break; + + case eRValueReferenceToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->CreateClangRValueReferenceType (encoding_type)); + break; + + default: + assert(!"Unhandled encoding_uid_type."); + break; + } + } + } + else + { + // We have no encoding type, return void? + void *void_clang_type = type_list->GetClangASTContext().GetVoidBuiltInType(); + switch (m_encoding_uid_type) + { + case eIsTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(void_clang_type); + break; + + case eIsConstTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr (ClangASTContext::AddConstModifier (void_clang_type)); + break; + + case eIsRestrictTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr (ClangASTContext::AddRestrictModifier (void_clang_type)); + break; + + case eIsVolatileTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr (ClangASTContext::AddVolatileModifier (void_clang_type)); + break; + + case eTypedefToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->GetClangASTContext().CreateTypedefType (m_name.AsCString(), void_clang_type, NULL)); + break; + + case ePointerToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->GetClangASTContext().CreatePointerType (void_clang_type)); + break; + + case eLValueReferenceToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->GetClangASTContext().CreateLValueReferenceType (void_clang_type)); + break; + + case eRValueReferenceToTypeWithUID: + resolved_qual_type = clang::QualType::getFromOpaquePtr(type_list->GetClangASTContext().CreateRValueReferenceType (void_clang_type)); + break; + + default: + assert(!"Unhandled encoding_uid_type."); + break; + } + } + if (resolved_qual_type.getTypePtr()) + { + m_clang_qual_type = resolved_qual_type.getAsOpaquePtr(); + } + + } + return m_clang_qual_type != NULL; +} + +void * +lldb_private::Type::GetChildClangTypeAtIndex +( + const char *parent_name, + uint32_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + ConstString& name, + uint32_t &child_byte_size, + int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset +) +{ + if (!ResolveClangType()) + return false; + + std::string name_str; + void *child_qual_type = GetClangASTContext().GetChildClangTypeAtIndex ( + parent_name, + m_clang_qual_type, + idx, + transparent_pointers, + omit_empty_base_classes, + name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + + if (child_qual_type) + { + if (!name_str.empty()) + name.SetCString(name_str.c_str()); + else + name.Clear(); + } + return child_qual_type; +} + + + +void * +lldb_private::Type::GetOpaqueClangQualType () +{ + ResolveClangType(); + return m_clang_qual_type; +} + +clang::ASTContext * +lldb_private::Type::GetClangAST () +{ + TypeList *type_list = GetTypeList(); + if (type_list) + return type_list->GetClangASTContext().getASTContext(); + return NULL; +} + +lldb_private::ClangASTContext & +lldb_private::Type::GetClangASTContext () +{ + return GetTypeList()->GetClangASTContext(); +} + +int +lldb_private::Type::Compare(const Type &a, const Type &b) +{ + // Just compare the UID values for now... + lldb::user_id_t a_uid = a.GetID(); + lldb::user_id_t b_uid = b.GetID(); + if (a_uid < b_uid) + return -1; + if (a_uid > b_uid) + return 1; + return 0; +// if (a.getQualType() == b.getQualType()) +// return 0; +} + diff --git a/lldb/source/Symbol/TypeList.cpp b/lldb/source/Symbol/TypeList.cpp new file mode 100644 index 000000000000..f037035ed215 --- /dev/null +++ b/lldb/source/Symbol/TypeList.cpp @@ -0,0 +1,239 @@ +//===-- TypeList.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 +#include + +// Other libraries and framework includes +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" + +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" + +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_ostream.h" + +// Project includes +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +TypeList::TypeList(const char *target_triple) : + m_types(), + m_ast(target_triple) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TypeList::~TypeList() +{ +} + +//---------------------------------------------------------------------- +// Add a base type to the type list +//---------------------------------------------------------------------- + +//struct CampareDCTypeBaton +//{ +// CampareDCTypeBaton(const std::vector& _types, const Type* _search_type) : +// types(_types), +// search_type(_search_type) +// { +// } +// const std::vector& types; +// const Type* search_type; +//}; +// +//static int +//compare_dc_type (const void *key, const void *arrmem) +//{ +// const Type* search_type = ((CampareDCTypeBaton*) key)->search_type; +// uint32_t curr_index = *(uint32_t *)arrmem; +// const Type* curr_type = ((CampareDCTypeBaton*) key)->types[curr_index].get(); +// Type::CompareState state; +// return Type::Compare(*search_type, *curr_type, state); +//} +// +//struct LessThanBinaryPredicate +//{ +// LessThanBinaryPredicate(const CampareDCTypeBaton& _compare_baton) : +// compare_baton(_compare_baton) +// { +// } +// +// bool operator() (uint32_t a, uint32_t b) const +// { +// Type::CompareState state; +// return Type::Compare(*compare_baton.search_type, *compare_baton.types[b].get(), state) < 0; +// } +// const CampareDCTypeBaton& compare_baton; +//}; + +TypeSP +TypeList::InsertUnique(TypeSP& type_sp) +{ +#if 0 +// Stream s(stdout); +// s << "TypeList::InsertUnique for type "; +// type_sp->Dump(s); +// s << "Current list:\n"; +// Dump(s); + + CampareDCTypeBaton compare_baton(m_types, type_sp.get()); + uint32_t* match_index_ptr = (uint32_t*)bsearch(&compare_baton, &m_sorted_indexes[0], m_sorted_indexes.size(), sizeof(uint32_t), compare_dc_type); + if (match_index_ptr) + { +// s << "returning existing type: " << (void *)m_types[*match_index_ptr].get() << "\n"; + return m_types[*match_index_ptr]; + } + + // Get the new index within the m_types array before we add the new type + uint32_t uniqued_type_index = m_types.size(); + // Add the new shared pointer to our type by appending it to the end of the types array + m_types.push_back(type_sp); + // Figure out what the sorted index of this new type should be + uint32_t fake_index = 0; + LessThanBinaryPredicate compare_func_obj(compare_baton); + std::vector::iterator insert_pos = std::upper_bound(m_sorted_indexes.begin(), m_sorted_indexes.end(), fake_index, compare_func_obj); + // Insert the sorted index into our sorted index array + m_sorted_indexes.insert(insert_pos, uniqued_type_index); +#else + // Just push each type on the back for now. We will worry about uniquing later + m_types.push_back (type_sp); +#endif +// s << "New list:\n"; +// Dump(s); + + return type_sp; +} + +//---------------------------------------------------------------------- +// Find a base type by its unique ID. +//---------------------------------------------------------------------- +TypeSP +TypeList::FindType(lldb::user_id_t uid) +{ + TypeSP type_sp; + iterator pos, end; + for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) + if ((*pos)->GetID() == uid) + return *pos; + + return type_sp; +} + +//---------------------------------------------------------------------- +// Find a type by name. +//---------------------------------------------------------------------- +TypeList +TypeList::FindTypes(const ConstString &name) +{ + TypeList types(m_ast.getTargetInfo()->getTriple().getTriple().c_str()); + iterator pos, end; + for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) + if ((*pos)->GetName() == name) + types.InsertUnique(*pos); + return types; +} + +void +TypeList::Clear() +{ + m_types.clear(); +} + +uint32_t +TypeList::GetSize() const +{ + return m_types.size(); +} + +TypeSP +TypeList::GetTypeAtIndex(uint32_t idx) +{ + TypeSP type_sp; + if (idx < m_types.size()) + type_sp = m_types[idx]; + return type_sp; +} + +void +TypeList::Dump(Stream *s, bool show_context) +{ +// std::vector::const_iterator pos, end; +// for (pos = end = m_sorted_indexes.begin(), end = m_sorted_indexes.end(); pos != end; ++pos) +// { +// m_types[*pos]->Dump(s, show_context); +// } + + m_ast.getASTContext()->getTranslationUnitDecl()->print(llvm::fouts(), 0); + const size_t num_types = m_types.size(); + for (size_t i=0; iDump(s, show_context); + } +// ASTContext *ast_context = GetClangASTContext ().getASTContext(); +// if (ast_context) +// ast_context->PrintStats(); +} + + +ClangASTContext & +TypeList::GetClangASTContext () +{ + return m_ast; +} + +void * +TypeList::CreateClangPointerType (Type *type) +{ + assert(type); + return m_ast.CreatePointerType(type->GetOpaqueClangQualType()); +} + +void * +TypeList::CreateClangTypedefType (Type *typedef_type, Type *base_type) +{ + assert(typedef_type && base_type); + return m_ast.CreateTypedefType(typedef_type->GetName().AsCString(), base_type->GetOpaqueClangQualType(), typedef_type->GetSymbolFile()->GetClangDeclContextForTypeUID(typedef_type->GetID())); +} + +void * +TypeList::CreateClangLValueReferenceType (Type *type) +{ + assert(type); + return m_ast.CreateLValueReferenceType(type->GetOpaqueClangQualType()); +} + +void * +TypeList::CreateClangRValueReferenceType (Type *type) +{ + assert(type); + return m_ast.CreateRValueReferenceType (type->GetOpaqueClangQualType()); +} + + + diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp new file mode 100644 index 000000000000..35dcabfdd591 --- /dev/null +++ b/lldb/source/Symbol/Variable.cpp @@ -0,0 +1,167 @@ +//===-- Variable.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/Symbol/Variable.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Variable constructor +//---------------------------------------------------------------------- +Variable::Variable(lldb::user_id_t uid, + const ConstString& name, + Type *type, + ValueType scope, + SymbolContextScope *context, + Declaration* decl_ptr, + const DWARFExpression& location, + bool external, + bool artificial) : + UserID(uid), + m_name(name), + m_type(type), + m_scope(scope), + m_context(context), + m_declaration(decl_ptr), + m_location(location), + m_external(external), + m_artificial(artificial) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Variable::~Variable() +{ +} + + +void +Variable::Dump(Stream *s, bool show_context) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + *s << "Variable" << (const UserID&)*this; + + if (m_name) + *s << ", name = \"" << m_name << "\""; + + if (m_type != NULL) + { + *s << ", type = " << (void*)m_type << " ("; + m_type->DumpTypeName(s); + s->PutChar(')'); + } + + if (m_scope != eValueTypeInvalid) + { + s->PutCString(", scope = "); + switch (m_scope) + { + case eValueTypeVariableGlobal: s->PutCString(m_external ? "global" : "static"); break; + case eValueTypeVariableArgument: s->PutCString("parameter"); break; + case eValueTypeVariableLocal: s->PutCString("local"); break; + default: *s << "??? (" << m_scope << ')'; + } + } + + if (show_context && m_context != NULL) + { + s->PutCString(", context = ( "); + m_context->DumpSymbolContext(s); + s->PutCString(" )"); + } + + m_declaration.Dump(s); + + if (m_location.IsValid()) + { + s->PutCString(", location = "); + m_location.GetDescription(s, lldb::eDescriptionLevelBrief); + } + + if (m_external) + s->PutCString(", external"); + + if (m_artificial) + s->PutCString(", artificial"); + + s->EOL(); +} + + +size_t +Variable::MemorySize() const +{ + return sizeof(Variable); +} + + +void +Variable::CalculateSymbolContext (SymbolContext *sc) +{ + if (m_context) + m_context->CalculateSymbolContext(sc); + else + sc->Clear(); +} + + +bool +Variable::IsInScope (StackFrame *frame) +{ + switch (m_scope) + { + case eValueTypeVariableGlobal: + // Globals and statics are always in scope. + return true; + + case eValueTypeVariableArgument: + case eValueTypeVariableLocal: + // Check if the location has a location list that describes the value + // of the variable with address ranges and different locations for each + // address range? + if (m_location.IsLocationList()) + { + // It is a location list. We just need to tell if the location + // list contains the current address when converted to a load + // address + return m_location.LocationListContainsLoadAddress (&frame->GetThread().GetProcess(), frame->GetPC()); + } + else + { + // We don't have a location list, we just need to see if the block + // that this variable was defined in is currently + Block *frame_block = frame->GetSymbolContext(eSymbolContextBlock).block; + if (frame_block) + { + SymbolContext variable_sc; + CalculateSymbolContext (&variable_sc); + if (variable_sc.function && variable_sc.block) + return variable_sc.block->ContainsBlockWithID (frame_block->GetID()); + } + } + break; + + default: + break; + } + return false; +} + diff --git a/lldb/source/Symbol/VariableList.cpp b/lldb/source/Symbol/VariableList.cpp new file mode 100644 index 000000000000..7f864f287ead --- /dev/null +++ b/lldb/source/Symbol/VariableList.cpp @@ -0,0 +1,116 @@ +//===-- VariableList.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/Symbol/VariableList.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/CompileUnit.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// VariableList constructor +//---------------------------------------------------------------------- +VariableList::VariableList() : + m_variables() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +VariableList::~VariableList() +{ +} + + +void +VariableList::AddVariable(VariableSP &variable_sp) +{ + m_variables.push_back(variable_sp); +} + + +void +VariableList::AddVariables(VariableList *variable_list) +{ + std::copy( variable_list->m_variables.begin(), // source begin + variable_list->m_variables.end(), // source end + back_inserter(m_variables)); // destination +} + + +void +VariableList::Clear() +{ + m_variables.clear(); +} + + + +VariableSP +VariableList::GetVariableAtIndex(uint32_t idx) +{ + VariableSP variable_sp; + if (idx < m_variables.size()) + variable_sp = m_variables[idx]; + return variable_sp; +} + + + +VariableSP +VariableList::FindVariable(const ConstString& name) +{ + VariableSP var_sp; + iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + if ((*pos)->GetName() == name) + { + var_sp = (*pos); + break; + } + } + return var_sp; +} + + +size_t +VariableList::MemorySize() const +{ + size_t mem_size = sizeof(VariableList); + const_iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + mem_size += (*pos)->MemorySize(); + return mem_size; +} + +size_t +VariableList::GetSize() const +{ + return m_variables.size(); +} + + +void +VariableList::Dump(Stream *s, bool show_context) const +{ +// s.Printf("%.*p: ", (int)sizeof(void*) * 2, this); +// s.Indent(); +// s << "VariableList\n"; + + const_iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + (*pos)->Dump(s, show_context); + } +} + diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp new file mode 100644 index 000000000000..20d35e3dbb00 --- /dev/null +++ b/lldb/source/Target/ABI.cpp @@ -0,0 +1,47 @@ +//===-- ABI.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/Target/ABI.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +ABI* +ABI::FindPlugin (const ConstString &triple) +{ + std::auto_ptr abi_ap; + ABICreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = PluginManager::GetABICreateCallbackAtIndex(idx)) != NULL; + ++idx) + { + abi_ap.reset (create_callback(triple)); + + if (abi_ap.get()) + return abi_ap.release(); + } + + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +ABI::ABI() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ABI::~ABI() +{ +} diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp new file mode 100644 index 000000000000..e79f8c243ef4 --- /dev/null +++ b/lldb/source/Target/ExecutionContext.cpp @@ -0,0 +1,107 @@ +//===-- ExecutionContext.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/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +ExecutionContext::ExecutionContext() : + target (NULL), + process (NULL), + thread (NULL), + frame (NULL) +{ +} + +ExecutionContext::ExecutionContext (Target* t, bool fill_current_process_thread_frame) : + target (t), + process (NULL), + thread (NULL), + frame (NULL) +{ + if (t && fill_current_process_thread_frame) + { + process = t->GetProcessSP().get(); + if (process) + { + thread = process->GetThreadList().GetCurrentThread().get(); + if (thread) + frame = thread->GetCurrentFrame().get(); + } + } +} + +ExecutionContext::ExecutionContext(Process* p, Thread *t, StackFrame *f) : + target (p ? &p->GetTarget() : NULL), + process (p), + thread (t), + frame (f) +{ +} + +ExecutionContext::ExecutionContext (ExecutionContextScope *exe_scope_ptr) +{ + if (exe_scope_ptr) + exe_scope_ptr->Calculate (*this); + else + { + target = NULL; + process = NULL; + thread = NULL; + frame = NULL; + } +} + +ExecutionContext::ExecutionContext (ExecutionContextScope &exe_scope_ref) +{ + exe_scope_ref.Calculate (*this); +} + +void +ExecutionContext::Clear() +{ + target = NULL; + process = NULL; + thread = NULL; + frame = NULL; +} + + +RegisterContext * +ExecutionContext::GetRegisterContext () const +{ + if (frame) + return frame->GetRegisterContext(); + else if (thread) + return thread->GetRegisterContext(); + return NULL; +} + +ExecutionContextScope * +ExecutionContext::GetBestExecutionContextScope () const +{ + if (frame) + return frame; + if (thread) + return thread; + if (process) + return process; + return target; +} diff --git a/lldb/source/Target/ObjCObjectPrinter.cpp b/lldb/source/Target/ObjCObjectPrinter.cpp new file mode 100644 index 000000000000..81c73aa95815 --- /dev/null +++ b/lldb/source/Target/ObjCObjectPrinter.cpp @@ -0,0 +1,120 @@ +//===-- ObjCObjectPrinter.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/StreamString.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "ObjCObjectPrinter.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ObjCObjectPrinter constructor +//---------------------------------------------------------------------- +ObjCObjectPrinter::ObjCObjectPrinter (Process &process) : + m_process(process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ObjCObjectPrinter::~ObjCObjectPrinter () +{ +} + +bool +ObjCObjectPrinter::PrintObject (ConstString &str, Value &object_ptr, ExecutionContext &exe_ctx) +{ + if (!exe_ctx.process) + return false; + + const Address *function_address = GetPrintForDebuggerAddr(); + + if (!function_address) + return false; + + const char *target_triple = exe_ctx.process->GetTargetTriple().GetCString(); + ClangASTContext *ast_context = exe_ctx.target->GetScratchClangASTContext(); + + void *return_qualtype = ast_context->GetCStringType(true); + Value ret; + ret.SetContext(Value::eContextTypeOpaqueClangQualType, return_qualtype); + + ValueList arg_value_list; + arg_value_list.PushValue(object_ptr); + + ClangFunction func(target_triple, ast_context, return_qualtype, *function_address, arg_value_list); + StreamString error_stream; + + lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; + func.InsertFunction(exe_ctx, wrapper_struct_addr, error_stream); + // FIXME: Check result of ExecuteFunction. + func.ExecuteFunction(exe_ctx, &wrapper_struct_addr, error_stream, true, 1000, true, ret); + + addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + + // poor man's strcpy + + size_t len = 0; + bool keep_reading = true; + Error error; + while (keep_reading) + { + char byte; + + if (exe_ctx.process->ReadMemory(result_ptr + len, &byte, 1, error) != 1) + return false; + + if (byte == '\0') + keep_reading = false; + else + ++len; + } + + char desc[len + 1]; + + if (exe_ctx.process->ReadMemory(result_ptr, &desc[0], len + 1, error) != len + 1) + return false; + + str.SetCString(desc); + + return true; +} + +Address * +ObjCObjectPrinter::GetPrintForDebuggerAddr() +{ + if (!m_PrintForDebugger_addr.get()) + { + ModuleList &modules = m_process.GetTarget().GetImages(); + + SymbolContextList contexts; + SymbolContext context; + + if((!modules.FindSymbolsWithNameAndType(ConstString ("_NSPrintForDebugger"), eSymbolTypeCode, contexts)) && + (!modules.FindSymbolsWithNameAndType(ConstString ("_CFPrintForDebugger"), eSymbolTypeCode, contexts))) + return NULL; + + contexts.GetContextAtIndex(0, context); + + m_PrintForDebugger_addr.reset(new Address(context.symbol->GetValue())); + } + + return m_PrintForDebugger_addr.get(); +} + diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp new file mode 100644 index 000000000000..02ca0671ca05 --- /dev/null +++ b/lldb/source/Target/PathMappingList.cpp @@ -0,0 +1,125 @@ +//===-- PathMappingList.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/Error.h" +#include "lldb/Core/Stream.h" +// Project includes +#include "PathMappingList.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// PathMappingList constructor +//---------------------------------------------------------------------- +PathMappingList::PathMappingList +( + ChangedCallback callback, + void *callback_baton +) : + m_pairs (), + m_callback (callback), + m_callback_baton (callback_baton) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +PathMappingList::~PathMappingList () +{ +} + +void +PathMappingList::Append (const ConstString &path, + const ConstString &replacement, + bool notify) +{ + m_pairs.push_back(pair(path, replacement)); + if (notify && m_callback) + m_callback (*this, m_callback_baton); +} + +void +PathMappingList::Insert (const ConstString &path, + const ConstString &replacement, + uint32_t index, + bool notify) +{ + iterator insert_iter; + if (index >= m_pairs.size()) + insert_iter = m_pairs.end(); + else + insert_iter = m_pairs.begin() + index; + m_pairs.insert(insert_iter, pair(path, replacement)); + if (notify && m_callback) + m_callback (*this, m_callback_baton); +} + +bool +PathMappingList::Remove (off_t index, bool notify) +{ + if (index >= m_pairs.size()) + return false; + + iterator iter = m_pairs.begin() + index; + m_pairs.erase(iter); + if (notify && m_callback) + m_callback (*this, m_callback_baton); + return true; +} + +void +PathMappingList::Dump (Stream *s) +{ + unsigned int numPairs = m_pairs.size(); + unsigned int index; + + for (index = 0; index < numPairs; ++index) + { + s->Printf("[%d] \"%s\" -> \"%s\"\n", + index, m_pairs[index].first.GetCString(), m_pairs[index].second.GetCString()); + } +} + +void +PathMappingList::Clear (bool notify) +{ + m_pairs.clear(); + if (notify && m_callback) + m_callback (*this, m_callback_baton); +} + +size_t +PathMappingList::GetSize () +{ + return m_pairs.size(); +} + +bool +PathMappingList::RemapPath (const ConstString &path, ConstString &new_path) +{ + const_iterator pos, end = m_pairs.end(); + for (pos = m_pairs.begin(); pos != end; ++pos) + { + const size_t prefixLen = pos->first.GetLength(); + + if (::strncmp (pos->first.GetCString(), path.GetCString(), prefixLen) == 0) + { + std::string new_path_str (pos->second.GetCString()); + new_path_str.append(path.GetCString() + prefixLen); + new_path.SetCString(new_path_str.c_str()); + return true; + } + } + return false; +} diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp new file mode 100644 index 000000000000..9a6ab0c83e87 --- /dev/null +++ b/lldb/source/Target/Process.cpp @@ -0,0 +1,1876 @@ +//===-- Process.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/Target/Process.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +using namespace lldb; +using namespace lldb_private; + +Process* +Process::FindPlugin (Target &target, const char *plugin_name, Listener &listener) +{ + ProcessCreateInstance create_callback = NULL; + if (plugin_name) + { + create_callback = PluginManager::GetProcessCreateCallbackForPluginName (plugin_name); + if (create_callback) + { + std::auto_ptr debugger_ap(create_callback(target, listener)); + if (debugger_ap->CanDebug(target)) + return debugger_ap.release(); + } + } + else + { + for (uint32_t idx = 0; create_callback = PluginManager::GetProcessCreateCallbackAtIndex(idx); ++idx) + { + create_callback = PluginManager::GetProcessCreateCallbackAtIndex (idx); + if (create_callback) + { + std::auto_ptr debugger_ap(create_callback(target, listener)); + if (debugger_ap->CanDebug(target)) + return debugger_ap.release(); + } + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// Process constructor +//---------------------------------------------------------------------- +Process::Process(Target &target, Listener &listener) : + UserID (LLDB_INVALID_PROCESS_ID), + Broadcaster ("Process"), + m_target (target), + m_section_load_info (), + m_public_state (eStateUnloaded), + m_private_state (eStateUnloaded), + m_private_state_broadcaster ("lldb.process.internal_state_broadcaster"), + m_private_state_control_broadcaster ("lldb.process.internal_state_control_broadcaster"), + m_private_state_listener ("lldb.process.internal_state_listener"), + m_private_state_control_wait(), + m_private_state_thread (LLDB_INVALID_HOST_THREAD), + m_stop_id (0), + m_thread_index_id (0), + m_exit_status (-1), + m_exit_string (), + m_thread_list (this), + m_notifications (), + m_listener(listener), + m_unix_signals (), + m_objc_object_printer(*this) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Process::Process()", this); + + listener.StartListeningForEvents (this, + eBroadcastBitStateChanged | + eBroadcastBitInterrupt | + eBroadcastBitSTDOUT | + eBroadcastBitSTDERR); + + m_private_state_listener.StartListeningForEvents(&m_private_state_broadcaster, + eBroadcastBitStateChanged); + + m_private_state_listener.StartListeningForEvents(&m_private_state_control_broadcaster, + eBroadcastInternalStateControlStop | + eBroadcastInternalStateControlPause | + eBroadcastInternalStateControlResume); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Process::~Process() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Process::~Process()", this); + StopPrivateStateThread(); +} + +void +Process::Finalize() +{ + // Do any cleanup needed prior to being destructed... Subclasses + // that override this method should call this superclass method as well. +} + +void +Process::RegisterNotificationCallbacks (const Notifications& callbacks) +{ + m_notifications.push_back(callbacks); + if (callbacks.initialize != NULL) + callbacks.initialize (callbacks.baton, this); +} + +bool +Process::UnregisterNotificationCallbacks(const Notifications& callbacks) +{ + std::vector::iterator pos, end = m_notifications.end(); + for (pos = m_notifications.begin(); pos != end; ++pos) + { + if (pos->baton == callbacks.baton && + pos->initialize == callbacks.initialize && + pos->process_state_changed == callbacks.process_state_changed) + { + m_notifications.erase(pos); + return true; + } + } + return false; +} + +void +Process::SynchronouslyNotifyStateChanged (StateType state) +{ + std::vector::iterator notification_pos, notification_end = m_notifications.end(); + for (notification_pos = m_notifications.begin(); notification_pos != notification_end; ++notification_pos) + { + if (notification_pos->process_state_changed) + notification_pos->process_state_changed (notification_pos->baton, this, state); + } +} + +// FIXME: We need to do some work on events before the general Listener sees them. +// For instance if we are continuing from a breakpoint, we need to ensure that we do +// the little "insert real insn, step & stop" trick. But we can't do that when the +// event is delivered by the broadcaster - since that is done on the thread that is +// waiting for new events, so if we needed more than one event for our handling, we would +// stall. So instead we do it when we fetch the event off of the queue. +// + +StateType +Process::GetNextEvent (EventSP &event_sp) +{ + StateType state = eStateInvalid; + + if (m_listener.GetNextEventForBroadcaster (this, event_sp) && event_sp) + state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + + return state; +} + + +StateType +Process::WaitForProcessToStop (const TimeValue *timeout) +{ + StateType match_states[] = { eStateStopped, eStateCrashed, eStateDetached, eStateExited, eStateUnloaded }; + return WaitForState (timeout, match_states, sizeof(match_states) / sizeof(StateType)); +} + + +StateType +Process::WaitForState +( + const TimeValue *timeout, + const StateType *match_states, const uint32_t num_match_states +) +{ + EventSP event_sp; + uint32_t i; + StateType state = eStateUnloaded; + while (state != eStateInvalid) + { + state = WaitForStateChangedEvents (timeout, event_sp); + + for (i=0; iPrintf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout); + + StateType state = eStateInvalid; + if (m_listener.WaitForEventForBroadcasterWithType(timeout, + this, + eBroadcastBitStateChanged, + event_sp)) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp) => %s", + __FUNCTION__, + timeout, + StateAsCString(state)); + return state; +} + +Event * +Process::PeekAtStateChangedEvents () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s...", __FUNCTION__); + + Event *event_ptr; + event_ptr = m_listener.PeekAtNextEventForBroadcasterWithType(this, + eBroadcastBitStateChanged); + if (log) + { + if (event_ptr) + { + log->Printf ("Process::%s (event_ptr) => %s", + __FUNCTION__, + StateAsCString(ProcessEventData::GetStateFromEvent (event_ptr))); + } + else + { + log->Printf ("Process::%s no events found", + __FUNCTION__); + } + } + return event_ptr; +} + +StateType +Process::WaitForStateChangedEventsPrivate (const TimeValue *timeout, EventSP &event_sp) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout); + + StateType state = eStateInvalid; + if (m_private_state_listener.WaitForEventForBroadcasterWithType(timeout, + &m_private_state_broadcaster, + eBroadcastBitStateChanged, + event_sp)) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + // This is a bit of a hack, but when we wait here we could very well return + // to the command-line, and that could disable the log, which would render the + // log we got above invalid. + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp) => %s", __FUNCTION__, timeout, StateAsCString(state)); + return state; +} + +bool +Process::WaitForEventsPrivate (const TimeValue *timeout, EventSP &event_sp, bool control_only) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout); + + if (control_only) + return m_private_state_listener.WaitForEventForBroadcaster(timeout, &m_private_state_control_broadcaster, event_sp); + else + return m_private_state_listener.WaitForEvent(timeout, event_sp); +} + +bool +Process::IsRunning () const +{ + return StateIsRunningState (m_public_state.GetValue()); +} + +int +Process::GetExitStatus () +{ + if (m_public_state.GetValue() == eStateExited) + return m_exit_status; + return -1; +} + +const char * +Process::GetExitDescription () +{ + if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) + return m_exit_string.c_str(); + return NULL; +} + +void +Process::SetExitStatus (int status, const char *cstr) +{ + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + + SetPrivateState (eStateExited); +} + +// This static callback can be used to watch for local child processes on +// the current host. The the child process exits, the process will be +// found in the global target list (we want to be completely sure that the +// lldb_private::Process doesn't go away before we can deliver the signal. +bool +Process::SetProcessExitStatus +( + void *callback_baton, + lldb::pid_t pid, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero +) +{ + if (signo == 0 || exit_status) + { + TargetSP target_sp(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcessID (pid)); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + { + const char *signal_cstr = NULL; + if (signo) + signal_cstr = process_sp->GetUnixSignals().GetSignalAsCString (signo); + + process_sp->SetExitStatus (exit_status, signal_cstr); + } + } + return true; + } + return false; +} + + +uint32_t +Process::GetNextThreadIndexID () +{ + return ++m_thread_index_id; +} + +StateType +Process::GetState() +{ + // If any other threads access this we will need a mutex for it + return m_public_state.GetValue (); +} + +void +Process::SetPublicState (StateType new_state) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE); + if (log) + log->Printf("Process::SetPublicState (%s)", StateAsCString(new_state)); + m_public_state.SetValue (new_state); +} + +StateType +Process::GetPrivateState () +{ + return m_private_state.GetValue(); +} + +void +Process::SetPrivateState (StateType new_state) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE); + bool state_changed = false; + + if (log) + log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state)); + + Mutex::Locker locker(m_private_state.GetMutex()); + + const StateType old_state = m_private_state.GetValueNoLock (); + state_changed = old_state != new_state; + if (state_changed) + { + m_private_state.SetValueNoLock (new_state); + if (StateIsStoppedState(new_state)) + { + m_stop_id++; + if (log) + log->Printf("Process::SetPrivateState (%s) stop_id = %u", StateAsCString(new_state), m_stop_id); + } + // Use our target to get a shared pointer to ourselves... + m_private_state_broadcaster.BroadcastEvent (eBroadcastBitStateChanged, new ProcessEventData (GetTarget().GetProcessSP(), new_state)); + } + else + { + if (log) + log->Printf("Process::SetPrivateState (%s) state didn't change. Ignoring...", StateAsCString(new_state), StateAsCString(old_state)); + } +} + + +uint32_t +Process::GetStopID() const +{ + return m_stop_id; +} + +addr_t +Process::GetImageInfoAddress() +{ + return LLDB_INVALID_ADDRESS; +} + +DynamicLoader * +Process::GetDynamicLoader() +{ + return NULL; +} + +const ABI * +Process::GetABI() +{ + ConstString& triple = m_target_triple; + + if (triple.IsEmpty()) + return NULL; + + if (m_abi_sp.get() == NULL) + { + m_abi_sp.reset(ABI::FindPlugin(triple)); + } + + return m_abi_sp.get(); +} + +BreakpointSiteList & +Process::GetBreakpointSiteList() +{ + return m_breakpoint_site_list; +} + +const BreakpointSiteList & +Process::GetBreakpointSiteList() const +{ + return m_breakpoint_site_list; +} + + +void +Process::DisableAllBreakpointSites () +{ + m_breakpoint_site_list.SetEnabledForAll (false); +} + +Error +Process::ClearBreakpointSiteByID (lldb::user_id_t break_id) +{ + Error error (DisableBreakpointSiteByID (break_id)); + + if (error.Success()) + m_breakpoint_site_list.Remove(break_id); + + return error; +} + +Error +Process::DisableBreakpointSiteByID (lldb::user_id_t break_id) +{ + Error error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id); + if (bp_site_sp) + { + if (bp_site_sp->IsEnabled()) + error = DisableBreakpoint (bp_site_sp.get()); + } + else + { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %i", break_id); + } + + return error; +} + +Error +Process::EnableBreakpointSiteByID (lldb::user_id_t break_id) +{ + Error error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id); + if (bp_site_sp) + { + if (!bp_site_sp->IsEnabled()) + error = EnableBreakpoint (bp_site_sp.get()); + } + else + { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %i", break_id); + } + return error; +} + +lldb::user_id_t +Process::CreateBreakpointSite (BreakpointLocationSP &owner, bool use_hardware) +{ + const addr_t load_addr = owner->GetAddress().GetLoadAddress (this); + if (load_addr != LLDB_INVALID_ADDRESS) + { + BreakpointSiteSP bp_site_sp; + + // Look up this breakpoint site. If it exists, then add this new owner, otherwise + // create a new breakpoint site and add it. + + bp_site_sp = m_breakpoint_site_list.FindByAddress (load_addr); + + if (bp_site_sp) + { + bp_site_sp->AddOwner (owner); + owner->SetBreakpointSite (bp_site_sp); + return bp_site_sp->GetID(); + } + else + { + bp_site_sp.reset (new BreakpointSite (&m_breakpoint_site_list, owner, load_addr, LLDB_INVALID_THREAD_ID, use_hardware)); + if (bp_site_sp) + { + if (EnableBreakpoint (bp_site_sp.get()).Success()) + { + owner->SetBreakpointSite (bp_site_sp); + return m_breakpoint_site_list.Add (bp_site_sp); + } + } + } + } + // We failed to enable the breakpoint + return LLDB_INVALID_BREAK_ID; + +} + +void +Process::RemoveOwnerFromBreakpointSite (lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, BreakpointSiteSP &bp_site_sp) +{ + uint32_t num_owners = bp_site_sp->RemoveOwner (owner_id, owner_loc_id); + if (num_owners == 0) + { + DisableBreakpoint(bp_site_sp.get()); + m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress()); + } +} + + +size_t +Process::RemoveBreakpointOpcodesFromBuffer (addr_t bp_addr, size_t size, uint8_t *buf) const +{ + size_t bytes_removed = 0; + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + size_t idx; + BreakpointSiteSP bp; + + for (idx = 0; (bp = m_breakpoint_site_list.GetByIndex(idx)) != NULL; ++idx) + { + if (bp->GetType() == BreakpointSite::eSoftware) + { + if (bp->IntersectsRange(bp_addr, size, &intersect_addr, &intersect_size, &opcode_offset)) + { + assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size); + assert(bp_addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= bp_addr + size); + assert(opcode_offset + intersect_size <= bp->GetByteSize()); + size_t buf_offset = intersect_addr - bp_addr; + ::memcpy(buf + buf_offset, bp->GetSavedOpcodeBytes() + opcode_offset, intersect_size); + } + } + } + return bytes_removed; +} + + +Error +Process::EnableSoftwareBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + const addr_t bp_addr = bp_site->GetLoadAddress(); + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx", bp_site->GetID(), (uint64_t)bp_addr); + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- already enabled", bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + if (bp_addr == LLDB_INVALID_ADDRESS) + { + error.SetErrorString("BreakpointSite contains an invalid load address."); + return error; + } + // Ask the lldb::Process subclass to fill in the correct software breakpoint + // trap for the breakpoint site + const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site); + + if (bp_opcode_size == 0) + { + error.SetErrorStringWithFormat ("Process::GetSoftwareBreakpointTrapOpcode() returned zero, unable to get breakpoint trap for address 0x%llx.\n", bp_addr); + } + else + { + const uint8_t * const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes(); + + if (bp_opcode_bytes == NULL) + { + error.SetErrorString ("BreakpointSite doesn't contain a valid breakpoint trap opcode."); + return error; + } + + // Save the original opcode by reading it + if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, error) == bp_opcode_size) + { + // Write a software breakpoint in place of the original opcode + if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) + { + uint8_t verify_bp_opcode_bytes[64]; + if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) + { + if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, bp_opcode_size) == 0) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eSoftware); + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- SUCCESS", + bp_site->GetID(), + (uint64_t)bp_addr); + } + else + error.SetErrorString("Failed to verify the breakpoint trap in memory."); + } + else + error.SetErrorString("Unable to read memory to verify breakpoint trap."); + } + else + error.SetErrorString("Unable to write breakpoint trap to memory."); + } + else + error.SetErrorString("Unable to read memory at breakpoint address."); + } + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- FAILED: %s", + bp_site->GetID(), + (uint64_t)bp_addr, + error.AsCString()); + return error; +} + +Error +Process::DisableSoftwareBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + addr_t bp_addr = bp_site->GetLoadAddress(); + lldb::user_id_t breakID = bp_site->GetID(); + if (log) + log->Printf ("ProcessMacOSX::DisableBreakpoint (breakID = %d) addr = 0x%llx", breakID, (uint64_t)bp_addr); + + if (bp_site->IsHardware()) + { + error.SetErrorString("Breakpoint site is a hardware breakpoint."); + } + else if (bp_site->IsEnabled()) + { + const size_t break_op_size = bp_site->GetByteSize(); + const uint8_t * const break_op = bp_site->GetTrapOpcodeBytes(); + if (break_op_size > 0) + { + // Clear a software breakoint instruction + uint8_t curr_break_op[break_op_size]; + bool break_op_found = false; + + // Read the breakpoint opcode + if (DoReadMemory (bp_addr, curr_break_op, break_op_size, error) == break_op_size) + { + bool verify = false; + // Make sure we have the a breakpoint opcode exists at this address + if (::memcmp (curr_break_op, break_op, break_op_size) == 0) + { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (DoWriteMemory (bp_addr, bp_site->GetSavedOpcodeBytes(), break_op_size, error) == break_op_size) + { + verify = true; + } + else + error.SetErrorString("Memory write failed when restoring original opcode."); + } + else + { + error.SetErrorString("Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has already been restored + verify = true; + } + + if (verify) + { + uint8_t verify_opcode[break_op_size]; + // Verify that our original opcode made it back to the inferior + if (DoReadMemory (bp_addr, verify_opcode, break_op_size, error) == break_op_size) + { + // compare the memory we just read with the original opcode + if (::memcmp (bp_site->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0) + { + // SUCCESS + bp_site->SetEnabled(false); + if (log) + log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + else + { + if (break_op_found) + error.SetErrorString("Failed to restore original opcode."); + } + } + else + error.SetErrorString("Failed to read memory to verify that breakpoint trap was restored."); + } + } + else + error.SetErrorString("Unable to read memory that should contain the breakpoint trap."); + } + } + else + { + if (log) + log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- already disabled", bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + if (log) + log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- FAILED: %s", + bp_site->GetID(), + (uint64_t)bp_addr, + error.AsCString()); + return error; + +} + + +size_t +Process::ReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + if (buf == NULL || size == 0) + return 0; + + size_t bytes_read = 0; + uint8_t *bytes = (uint8_t *)buf; + + while (bytes_read < size) + { + const size_t curr_size = size - bytes_read; + const size_t curr_bytes_read = DoReadMemory (addr + bytes_read, + bytes + bytes_read, + curr_size, + error); + bytes_read += curr_bytes_read; + if (curr_bytes_read == curr_size || curr_bytes_read == 0) + break; + } + + // Replace any software breakpoint opcodes that fall into this range back + // into "buf" before we return + if (bytes_read > 0) + RemoveBreakpointOpcodesFromBuffer (addr, bytes_read, (uint8_t *)buf); + return bytes_read; +} + +size_t +Process::WriteMemoryPrivate (addr_t addr, const void *buf, size_t size, Error &error) +{ + size_t bytes_written = 0; + const uint8_t *bytes = (const uint8_t *)buf; + + while (bytes_written < size) + { + const size_t curr_size = size - bytes_written; + const size_t curr_bytes_written = DoWriteMemory (addr + bytes_written, + bytes + bytes_written, + curr_size, + error); + bytes_written += curr_bytes_written; + if (curr_bytes_written == curr_size || curr_bytes_written == 0) + break; + } + return bytes_written; +} + +size_t +Process::WriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + if (buf == NULL || size == 0) + return 0; + // We need to write any data that would go where any current software traps + // (enabled software breakpoints) any software traps (breakpoints) that we + // may have placed in our tasks memory. + + BreakpointSiteList::collection::const_iterator iter = m_breakpoint_site_list.GetMap()->lower_bound (addr); + BreakpointSiteList::collection::const_iterator end = m_breakpoint_site_list.GetMap()->end(); + + if (iter == end || iter->second->GetLoadAddress() > addr + size) + return DoWriteMemory(addr, buf, size, error); + + BreakpointSiteList::collection::const_iterator pos; + size_t bytes_written = 0; + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + const uint8_t *ubuf = (const uint8_t *)buf; + + for (pos = iter; pos != end; ++pos) + { + BreakpointSiteSP bp; + bp = pos->second; + + assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)); + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->GetByteSize()); + + // Check for bytes before this breakpoint + const addr_t curr_addr = addr + bytes_written; + if (intersect_addr > curr_addr) + { + // There are some bytes before this breakpoint that we need to + // just write to memory + size_t curr_size = intersect_addr - curr_addr; + size_t curr_bytes_written = WriteMemoryPrivate (curr_addr, + ubuf + bytes_written, + curr_size, + error); + bytes_written += curr_bytes_written; + if (curr_bytes_written != curr_size) + { + // We weren't able to write all of the requested bytes, we + // are done looping and will return the number of bytes that + // we have written so far. + break; + } + } + + // Now write any bytes that would cover up any software breakpoints + // directly into the breakpoint opcode buffer + ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); + bytes_written += intersect_size; + } + + // Write any remaining bytes after the last breakpoint if we have any left + if (bytes_written < size) + bytes_written += WriteMemoryPrivate (addr + bytes_written, + ubuf + bytes_written, + size - bytes_written, + error); + + return bytes_written; +} + +addr_t +Process::AllocateMemory(size_t size, uint32_t permissions, Error &error) +{ + // Fixme: we should track the blocks we've allocated, and clean them up... + // We could even do our own allocator here if that ends up being more efficient. + return DoAllocateMemory (size, permissions, error); +} + +Error +Process::DeallocateMemory (addr_t ptr) +{ + return DoDeallocateMemory (ptr); +} + + +Error +Process::EnableWatchpoint (WatchpointLocation *watchpoint) +{ + Error error; + error.SetErrorString("watchpoints are not supported"); + return error; +} + +Error +Process::DisableWatchpoint (WatchpointLocation *watchpoint) +{ + Error error; + error.SetErrorString("watchpoints are not supported"); + return error; +} + +StateType +Process::WaitForProcessStopPrivate (const TimeValue *timeout, EventSP &event_sp) +{ + StateType state; + // Now wait for the process to launch and return control to us, and then + // call DidLaunch: + while (1) + { + // FIXME: Might want to put a timeout in here: + state = WaitForStateChangedEventsPrivate (NULL, event_sp); + if (state == eStateStopped || state == eStateCrashed || state == eStateExited) + break; + else + HandlePrivateEvent (event_sp); + } + return state; +} + +Error +Process::Launch +( + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ + Error error; + m_target_triple.Clear(); + m_abi_sp.reset(); + + Module *exe_module = m_target.GetExecutableModule().get(); + if (exe_module) + { + char exec_file_path[PATH_MAX]; + exe_module->GetFileSpec().GetPath(exec_file_path, sizeof(exec_file_path)); + if (exe_module->GetFileSpec().Exists()) + { + error = WillLaunch (exe_module); + if (error.Success()) + { + // The args coming in should not contain the application name, the + // lldb_private::Process class will add this in case the executable + // gets resolved to a different file than was given on the command + // line (like when an applicaiton bundle is specified and will + // resolve to the contained exectuable file, or the file given was + // a symlink or other file system link that resolves to a different + // file). + + // Get the resolved exectuable path + + // Make a new argument vector + std::vector exec_path_plus_argv; + // Append the resolved executable path + exec_path_plus_argv.push_back (exec_file_path); + + // Push all args if there are any + if (argv) + { + for (int i = 0; argv[i]; ++i) + exec_path_plus_argv.push_back(argv[i]); + } + + // Push a NULL to terminate the args. + exec_path_plus_argv.push_back(NULL); + + // Now launch using these arguments. + error = DoLaunch (exe_module, exec_path_plus_argv.data(), envp, stdin_path, stdout_path, stderr_path); + + if (error.Fail()) + { + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + SetID (LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "launch failed"; + SetExitStatus (-1, error_string); + } + } + else + { + EventSP event_sp; + StateType state = WaitForProcessStopPrivate(NULL, event_sp); + + if (state == eStateStopped || state == eStateCrashed) + { + DidLaunch (); + + // This delays passing the stopped event to listeners till DidLaunch gets + // a chance to complete... + HandlePrivateEvent (event_sp); + StartPrivateStateThread (); + } + else if (state == eStateExited) + { + // We exited while trying to launch somehow. Don't call DidLaunch as that's + // not likely to work, and return an invalid pid. + HandlePrivateEvent (event_sp); + } + } + } + } + else + { + error.SetErrorStringWithFormat("File doesn't exist: '%s'.\n", exec_file_path); + } + } + return error; +} + +Error +Process::CompleteAttach () +{ + Error error; + EventSP event_sp; + StateType state = WaitForProcessStopPrivate(NULL, event_sp); + if (state == eStateStopped || state == eStateCrashed) + { + DidAttach (); + + // This delays passing the stopped event to listeners till DidLaunch gets + // a chance to complete... + HandlePrivateEvent(event_sp); + StartPrivateStateThread(); + } + else + { + // We exited while trying to launch somehow. Don't call DidLaunch as that's + // not likely to work, and return an invalid pid. + if (state == eStateExited) + HandlePrivateEvent (event_sp); + error.SetErrorStringWithFormat("invalid state after attach: %s", + lldb_private::StateAsCString(state)); + } + return error; +} + +Error +Process::Attach (lldb::pid_t attach_pid) +{ + + m_target_triple.Clear(); + m_abi_sp.reset(); + + Error error(WillAttach (attach_pid)); + if (error.Success()) + { + error = DoAttach (attach_pid); + if (error.Success()) + { + error = CompleteAttach(); + } + else + { + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + SetID (LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); + } + } + } + return error; +} + +Error +Process::Attach (const char *process_name, bool wait_for_launch) +{ + m_target_triple.Clear(); + m_abi_sp.reset(); + + Error error (WillAttach (process_name, wait_for_launch)); + if (error.Success()) + { + StartPrivateStateThread(); + error = DoAttach (process_name, wait_for_launch); + if (error.Fail()) + { + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + SetID (LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); + } + } + else + { + error = CompleteAttach(); + } + } + return error; +} + +Error +Process::Resume () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf("Process::Resume() m_stop_id = %u", m_stop_id); + + Error error (WillResume()); + // Tell the process it is about to resume before the thread list + if (error.Success()) + { + // Now let the thread list know we are about to resume to it + // can let all of our threads know that they are about to be + // resumed. Threads will each be called with + // Thread::WillResume(StateType) where StateType contains the state + // that they are supposed to have when the process is resumed + // (suspended/running/stepping). Threads should also check + // their resume signal in lldb::Thread::GetResumeSignal() + // to see if they are suppoed to start back up with a signal. + if (m_thread_list.WillResume()) + { + error = DoResume(); + if (error.Success()) + { + DidResume(); + m_thread_list.DidResume(); + } + } + else + { + error.SetErrorStringWithFormat("thread list returned flase after WillResume"); + } + } + return error; +} + +Error +Process::Halt () +{ + Error error (WillHalt()); + + if (error.Success()) + { + error = DoHalt(); + if (error.Success()) + DidHalt(); + } + return error; +} + +Error +Process::Detach () +{ + Error error (WillDetach()); + + if (error.Success()) + { + DisableAllBreakpointSites(); + error = DoDetach(); + if (error.Success()) + { + DidDetach(); + StopPrivateStateThread(); + } + } + return error; +} + +Error +Process::Destroy () +{ + Error error (WillDestroy()); + if (error.Success()) + { + DisableAllBreakpointSites(); + error = DoDestroy(); + if (error.Success()) + { + DidDestroy(); + StopPrivateStateThread(); + } + } + return error; +} + +Error +Process::Signal (int signal) +{ + Error error (WillSignal()); + if (error.Success()) + { + error = DoSignal(signal); + if (error.Success()) + DidSignal(); + } + return error; +} + +UnixSignals & +Process::GetUnixSignals () +{ + return m_unix_signals; +} + +Target & +Process::GetTarget () +{ + return m_target; +} + +const Target & +Process::GetTarget () const +{ + return m_target; +} + +uint32_t +Process::GetAddressByteSize() +{ + return m_target.GetArchitecture().GetAddressByteSize(); +} + +bool +Process::ShouldBroadcastEvent (Event *event_ptr) +{ + const StateType state = Process::ProcessEventData::GetStateFromEvent (event_ptr); + bool return_value = true; + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); + + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateDetached: + case eStateExited: + case eStateUnloaded: + // These events indicate changes in the state of the debugging session, always report them. + return_value = true; + break; + case eStateInvalid: + // We stopped for no apparent reason, don't report it. + return_value = false; + break; + case eStateRunning: + case eStateStepping: + // If we've started the target running, we handle the cases where we + // are already running and where there is a transition from stopped to + // running differently. + // running -> running: Automatically suppress extra running events + // stopped -> running: Report except when there is one or more no votes + // and no yes votes. + SynchronouslyNotifyStateChanged (state); + switch (m_public_state.GetValue()) + { + case eStateRunning: + case eStateStepping: + // We always suppress multiple runnings with no PUBLIC stop in between. + return_value = false; + break; + default: + // TODO: make this work correctly. For now always report + // run if we aren't running so we don't miss any runnning + // events. If I run the lldb/test/thread/a.out file and + // break at main.cpp:58, run and hit the breakpoints on + // multiple threads, then somehow during the stepping over + // of all breakpoints no run gets reported. + return_value = true; + + // This is a transition from stop to run. + switch (m_thread_list.ShouldReportRun (event_ptr)) + { + case eVoteYes: + case eVoteNoOpinion: + return_value = true; + break; + case eVoteNo: + return_value = false; + break; + } + break; + } + break; + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + { + // We've stopped. First see if we're going to restart the target. + // If we are going to stop, then we always broadcast the event. + // If we aren't going to stop, let the thread plans decide if we're going to report this event. + // If no thread has an opinion, we also report it. + if (state != eStateInvalid) + { + + RefreshStateAfterStop (); + + if (m_thread_list.ShouldStop (event_ptr) == false) + { + switch (m_thread_list.ShouldReportStop (event_ptr)) + { + case eVoteYes: + Process::ProcessEventData::SetRestartedInEvent (event_ptr, true); + case eVoteNoOpinion: + return_value = true; + break; + case eVoteNo: + return_value = false; + break; + } + + if (log) + log->Printf ("Process::ShouldBroadcastEvent (%p) Restarting process", event_ptr, StateAsCString(state)); + Resume (); + } + else + { + return_value = true; + SynchronouslyNotifyStateChanged (state); + } + } + } + } + + if (log) + log->Printf ("Process::ShouldBroadcastEvent (%p) => %s", event_ptr, StateAsCString(state), return_value ? "YES" : "NO"); + return return_value; +} + +//------------------------------------------------------------------ +// Thread Queries +//------------------------------------------------------------------ + +ThreadList & +Process::GetThreadList () +{ + return m_thread_list; +} + +const ThreadList & +Process::GetThreadList () const +{ + return m_thread_list; +} + + +bool +Process::StartPrivateStateThread () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); + + if (log) + log->Printf ("Process::%s ( )", __FUNCTION__); + + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + m_private_state_thread = Host::ThreadCreate ("", Process::PrivateStateThread, this, NULL); + return m_private_state_thread != LLDB_INVALID_HOST_THREAD; +} + +void +Process::PausePrivateStateThread () +{ + ControlPrivateStateThread (eBroadcastInternalStateControlPause); +} + +void +Process::ResumePrivateStateThread () +{ + ControlPrivateStateThread (eBroadcastInternalStateControlResume); +} + +void +Process::StopPrivateStateThread () +{ + ControlPrivateStateThread (eBroadcastInternalStateControlStop); +} + +void +Process::ControlPrivateStateThread (uint32_t signal) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); + + assert (signal == eBroadcastInternalStateControlStop || + signal == eBroadcastInternalStateControlPause || + signal == eBroadcastInternalStateControlResume); + + if (log) + log->Printf ("Process::%s ( ) - signal: %d", __FUNCTION__, signal); + + // Signal the private state thread + if (m_private_state_thread != LLDB_INVALID_HOST_THREAD) + { + TimeValue timeout_time; + bool timed_out; + + m_private_state_control_broadcaster.BroadcastEvent (signal, NULL); + + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + m_private_state_control_wait.WaitForValueEqualTo (true, &timeout_time, &timed_out); + m_private_state_control_wait.SetValue (false, eBroadcastNever); + + if (signal == eBroadcastInternalStateControlStop) + { + if (timed_out) + Host::ThreadCancel (m_private_state_thread, NULL); + + thread_result_t result = NULL; + Host::ThreadJoin (m_private_state_thread, &result, NULL); + } + } +} + +void +Process::HandlePrivateEvent (EventSP &event_sp) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + // See if we should broadcast this state to external clients? + const bool should_broadcast = ShouldBroadcastEvent (event_sp.get()); + if (log) + log->Printf ("Process::%s (arg = %p, pid = %i) got event '%s' broadcast = %s", __FUNCTION__, this, GetID(), StateAsCString(internal_state), should_broadcast ? "yes" : "no"); + + if (should_broadcast) + { + if (log) + { + log->Printf ("\tChanging public state from: %s to %s", StateAsCString(GetState ()), StateAsCString (internal_state)); + } + Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); + BroadcastEvent (event_sp); + } + else + { + if (log) + { + log->Printf ("\tNot changing public state with event: %s", StateAsCString (internal_state)); + } + } +} + +void * +Process::PrivateStateThread (void *arg) +{ + Process *proc = static_cast (arg); + void *result = proc->RunPrivateStateThread (); + proc->m_private_state_thread = LLDB_INVALID_HOST_THREAD; + return result; +} + +void * +Process::RunPrivateStateThread () +{ + bool control_only = false; + m_private_state_control_wait.SetValue (false, eBroadcastNever); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("Process::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, this, GetID()); + + bool exit_now = false; + while (!exit_now) + { + EventSP event_sp; + WaitForEventsPrivate (NULL, event_sp, control_only); + if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) + { + switch (event_sp->GetType()) + { + case eBroadcastInternalStateControlStop: + exit_now = true; + continue; // Go to next loop iteration so we exit without + break; // doing any internal state managment below + + case eBroadcastInternalStateControlPause: + control_only = true; + break; + + case eBroadcastInternalStateControlResume: + control_only = false; + break; + } + m_private_state_control_wait.SetValue (true, eBroadcastAlways); + } + + + const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (internal_state != eStateInvalid) + { + HandlePrivateEvent (event_sp); + } + + if (internal_state == eStateInvalid || internal_state == eStateExited) + break; + } + + if (log) + log->Printf ("Process::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, this, GetID()); + + return NULL; +} + +addr_t +Process::GetSectionLoadAddress (const Section *section) const +{ + // TODO: add support for the same section having multiple load addresses + addr_t section_load_addr = LLDB_INVALID_ADDRESS; + if (m_section_load_info.GetFirstKeyForValue (section, section_load_addr)) + return section_load_addr; + return LLDB_INVALID_ADDRESS; +} + +bool +Process::SectionLoaded (const Section *section, addr_t load_addr) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_SHLIB | LIBLLDB_LOG_VERBOSE); + + if (log) + log->Printf ("Process::%s (section = %p (%s.%s), load_addr = 0x%16.16llx)", + __FUNCTION__, + section, + section->GetModule()->GetFileSpec().GetFilename().AsCString(), + section->GetName().AsCString(), + load_addr); + + + const Section *existing_section = NULL; + Mutex::Locker locker(m_section_load_info.GetMutex()); + + if (m_section_load_info.GetValueForKeyNoLock (load_addr, existing_section)) + { + if (existing_section == section) + return false; // No change + } + m_section_load_info.SetValueForKeyNoLock (load_addr, section); + return true; // Changed +} + +size_t +Process::SectionUnloaded (const Section *section) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_SHLIB | LIBLLDB_LOG_VERBOSE); + + if (log) + log->Printf ("Process::%s (section = %p (%s.%s))", + __FUNCTION__, + section, + section->GetModule()->GetFileSpec().GetFilename().AsCString(), + section->GetName().AsCString()); + + Mutex::Locker locker(m_section_load_info.GetMutex()); + + size_t unload_count = 0; + addr_t section_load_addr; + while (m_section_load_info.GetFirstKeyForValueNoLock (section, section_load_addr)) + { + unload_count += m_section_load_info.EraseNoLock (section_load_addr); + } + return unload_count; +} + +bool +Process::SectionUnloaded (const Section *section, addr_t load_addr) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_SHLIB | LIBLLDB_LOG_VERBOSE); + + if (log) + log->Printf ("Process::%s (section = %p (%s.%s), load_addr = 0x%16.16llx)", + __FUNCTION__, + section, + section->GetModule()->GetFileSpec().GetFilename().AsCString(), + section->GetName().AsCString(), + load_addr); + + return m_section_load_info.Erase (load_addr) == 1; +} + + +bool +Process::ResolveLoadAddress (addr_t load_addr, Address &so_addr) const +{ + addr_t section_load_addr = LLDB_INVALID_ADDRESS; + const Section *section = NULL; + + // First find the top level section that this load address exists in + if (m_section_load_info.LowerBound (load_addr, section_load_addr, section, true)) + { + addr_t offset = load_addr - section_load_addr; + if (offset < section->GetByteSize()) + { + // We have found the top level section, now we need to find the + // deepest child section. + return section->ResolveContainedAddress (offset, so_addr); + } + } + so_addr.Clear(); + return false; +} + +//------------------------------------------------------------------ +// Process Event Data +//------------------------------------------------------------------ + +Process::ProcessEventData::ProcessEventData () : + EventData (), + m_process_sp (), + m_state (eStateInvalid), + m_update_state (false), + m_restarted (false) +{ +} + +Process::ProcessEventData::ProcessEventData (const ProcessSP &process_sp, StateType state) : + EventData (), + m_process_sp (process_sp), + m_state (state), + m_update_state (false), + m_restarted (false) +{ +} + +Process::ProcessEventData::~ProcessEventData() +{ +} + +const ConstString & +Process::ProcessEventData::GetFlavorString () +{ + static ConstString g_flavor ("Process::ProcessEventData"); + return g_flavor; +} + +const ConstString & +Process::ProcessEventData::GetFlavor () const +{ + return ProcessEventData::GetFlavorString (); +} + +const ProcessSP & +Process::ProcessEventData::GetProcessSP () const +{ + return m_process_sp; +} + +StateType +Process::ProcessEventData::GetState () const +{ + return m_state; +} + +bool +Process::ProcessEventData::GetRestarted () const +{ + return m_restarted; +} + +void +Process::ProcessEventData::SetRestarted (bool new_value) +{ + m_restarted = new_value; +} + +void +Process::ProcessEventData::DoOnRemoval (Event *event_ptr) +{ + // This function gets called twice for each event, once when the event gets pulled + // off of the private process event queue, and once when it gets pulled off of + // the public event queue. m_update_state is used to distinguish these + // two cases; it is false when we're just pulling it off for private handling, + // and we don't want to do the breakpoint command handling then. + + if (!m_update_state) + return; + + m_process_sp->SetPublicState (m_state); + + // If we're stopped and haven't restarted, then do the breakpoint commands here: + if (m_state == eStateStopped && ! m_restarted) + { + int num_threads = m_process_sp->GetThreadList().GetSize(); + int idx; + + for (idx = 0; idx < num_threads; ++idx) + { + lldb::ThreadSP thread_sp = m_process_sp->GetThreadList().GetThreadAtIndex(idx); + + Thread::StopInfo stop_info; + if (thread_sp->GetStopInfo(&stop_info)) + { + StopReason reason = stop_info.GetStopReason(); + if (reason == eStopReasonBreakpoint) + { + BreakpointSiteSP bp_site_sp; + // Look up the breakpoint site in the stop info, but the breakpoint + // might be a temporary one that's been deleted between the time we + // hit the breakpoint and now, if so there's nothing to do. + + bp_site_sp = m_process_sp->GetBreakpointSiteList().FindByID (stop_info.GetBreakpointSiteID()); + if (bp_site_sp) + { + size_t num_owners = bp_site_sp->GetNumberOfOwners(); + for (size_t j = 0; j < num_owners; j++) + { + lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j); + StoppointCallbackContext context (event_ptr, + m_process_sp.get(), + thread_sp.get(), + thread_sp->GetStackFrameAtIndex(0).get(), + false); + bp_loc_sp->InvokeCallback (&context); + } + } + else + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s could not find breakpoint site id: %d...", __FUNCTION__, stop_info.GetBreakpointSiteID()); + } + + } + } + } + if (m_process_sp->GetPrivateState() == eStateRunning) + SetRestarted(true); + } +} + +void +Process::ProcessEventData::Dump (Stream *s) const +{ + if (m_process_sp) + s->Printf(" process = %p (pid = %u), ", m_process_sp.get(), m_process_sp->GetID()); + + s->Printf("state = %s", StateAsCString(GetState()));; +} + +const Process::ProcessEventData * +Process::ProcessEventData::GetEventDataFromEvent (const Event *event_ptr) +{ + if (event_ptr) + { + const EventData *event_data = event_ptr->GetData(); + if (event_data && event_data->GetFlavor() == ProcessEventData::GetFlavorString()) + return static_cast (event_ptr->GetData()); + } + return NULL; +} + +ProcessSP +Process::ProcessEventData::GetProcessFromEvent (const Event *event_ptr) +{ + ProcessSP process_sp; + const ProcessEventData *data = GetEventDataFromEvent (event_ptr); + if (data) + process_sp = data->GetProcessSP(); + return process_sp; +} + +StateType +Process::ProcessEventData::GetStateFromEvent (const Event *event_ptr) +{ + const ProcessEventData *data = GetEventDataFromEvent (event_ptr); + if (data == NULL) + return eStateInvalid; + else + return data->GetState(); +} + +bool +Process::ProcessEventData::GetRestartedFromEvent (const Event *event_ptr) +{ + const ProcessEventData *data = GetEventDataFromEvent (event_ptr); + if (data == NULL) + return false; + else + return data->GetRestarted(); +} + +void +Process::ProcessEventData::SetRestartedInEvent (Event *event_ptr, bool new_value) +{ + ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); + if (data != NULL) + data->SetRestarted(new_value); +} + +bool +Process::ProcessEventData::SetUpdateStateOnRemoval (Event *event_ptr) +{ + ProcessEventData *data = const_cast(GetEventDataFromEvent (event_ptr)); + if (data) + { + data->SetUpdateStateOnRemoval(); + return true; + } + return false; +} + +void +Process::ProcessEventData::SetUpdateStateOnRemoval() +{ + m_update_state = true; +} + +Target * +Process::CalculateTarget () +{ + return &m_target; +} + +Process * +Process::CalculateProcess () +{ + return this; +} + +Thread * +Process::CalculateThread () +{ + return NULL; +} + +StackFrame * +Process::CalculateStackFrame () +{ + return NULL; +} + +void +Process::Calculate (ExecutionContext &exe_ctx) +{ + exe_ctx.target = &m_target; + exe_ctx.process = this; + exe_ctx.thread = NULL; + exe_ctx.frame = NULL; +} + +lldb::ProcessSP +Process::GetSP () +{ + return GetTarget().GetProcessSP(); +} + +ObjCObjectPrinter & +Process::GetObjCObjectPrinter() +{ + return m_objc_object_printer; +} + diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp new file mode 100644 index 000000000000..b1838ba477a1 --- /dev/null +++ b/lldb/source/Target/RegisterContext.cpp @@ -0,0 +1,238 @@ +//===-- RegisterContext.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/Target/RegisterContext.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RegisterContext constructor +//---------------------------------------------------------------------- +RegisterContext::RegisterContext (Thread &thread, StackFrame *frame) : + m_thread (thread), + m_frame (frame) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RegisterContext::~RegisterContext() +{ +} + +const RegisterInfo * +RegisterContext::GetRegisterInfoByName (const char *reg_name, uint32_t start_idx) +{ + if (reg_name && reg_name[0]) + { + const uint32_t num_registers = GetRegisterCount(); + for (uint32_t reg = start_idx; reg < num_registers; ++reg) + { + const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); + + if ((reg_info->name != NULL && ::strcasecmp (reg_info->name, reg_name) == 0) || + (reg_info->alt_name != NULL && ::strcasecmp (reg_info->alt_name, reg_name) == 0)) + { + return reg_info; + } + } + } + return NULL; +} + +const char * +RegisterContext::GetRegisterName (uint32_t reg) +{ + const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info) + return reg_info->name; + return NULL; +} + +uint64_t +RegisterContext::GetPC(uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +bool +RegisterContext::SetPC(uint64_t pc) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + bool success = WriteRegisterFromUnsigned (reg, pc); + if (success) + { + if (m_frame) + m_frame->ChangePC(pc); + else + m_thread.ClearStackFrames (); + } + return success; +} + +uint64_t +RegisterContext::GetSP(uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +bool +RegisterContext::SetSP(uint64_t sp) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + return WriteRegisterFromUnsigned (reg, sp); +} + +uint64_t +RegisterContext::GetFP(uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +bool +RegisterContext::SetFP(uint64_t fp) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + return WriteRegisterFromUnsigned (reg, fp); +} + +uint64_t +RegisterContext::GetReturnAddress (uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +uint64_t +RegisterContext::GetFlags (uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return ReadRegisterAsUnsigned (reg, fail_value); +} + + +uint64_t +RegisterContext::ReadRegisterAsUnsigned (uint32_t reg, uint64_t fail_value) +{ + if (reg != LLDB_INVALID_REGNUM) + { + Scalar value; + if (ReadRegisterValue (reg, value)) + return value.GetRawBits64(fail_value); + } + return fail_value; +} + +bool +RegisterContext::WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval) +{ + if (reg == LLDB_INVALID_REGNUM) + return false; + Scalar value(uval); + return WriteRegisterValue (reg, value); +} + +lldb::tid_t +RegisterContext::GetThreadID() const +{ + return m_thread.GetID(); +} + +uint32_t +RegisterContext::NumSupportedHardwareBreakpoints () +{ + return 0; +} + +uint32_t +RegisterContext::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContext::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + return false; +} + + +uint32_t +RegisterContext::NumSupportedHardwareWatchpoints () +{ + return 0; +} + +uint32_t +RegisterContext::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write) +{ + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContext::ClearHardwareWatchpoint (uint32_t hw_index) +{ + return false; +} + +bool +RegisterContext::HardwareSingleStep (bool enable) +{ + return false; +} + +Target * +RegisterContext::CalculateTarget () +{ + return m_thread.CalculateTarget(); +} + + +Process * +RegisterContext::CalculateProcess () +{ + return m_thread.CalculateProcess (); +} + +Thread * +RegisterContext::CalculateThread () +{ + return &m_thread; +} + +StackFrame * +RegisterContext::CalculateStackFrame () +{ + return m_frame; +} + +void +RegisterContext::Calculate (ExecutionContext &exe_ctx) +{ + if (m_frame) + m_frame->Calculate (exe_ctx); + else + m_thread.Calculate (exe_ctx); +} + + + diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp new file mode 100644 index 000000000000..cb832951661c --- /dev/null +++ b/lldb/source/Target/StackFrame.cpp @@ -0,0 +1,393 @@ +//===-- StackFrame.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/Target/StackFrame.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Value.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" + +using namespace lldb; +using namespace lldb_private; + +// The first bits in the flags are reserved for the SymbolContext::Scope bits +// so we know if we have tried to look up information in our internal symbol +// context (m_sc) already. +#define RESOLVED_PC_SO_ADDR (uint32_t(eSymbolContextEverything + 1)) +#define RESOLVED_FRAME_ID (RESOLVED_PC_SO_ADDR << 1) +#define GOT_FRAME_BASE (RESOLVED_FRAME_ID << 1) +#define FRAME_IS_OBSOLETE (GOT_FRAME_BASE << 1) +#define RESOLVED_VARIABLES (FRAME_IS_OBSOLETE << 1) + +StackFrame::StackFrame (lldb::user_id_t frame_idx, Thread &thread, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr) : + UserID (frame_idx), + m_thread (thread), + m_reg_context_sp(), + m_id(cfa), + m_pc(NULL, pc), + m_sc(), + m_flags(), + m_frame_base(), + m_frame_base_error(), + m_variable_list_sp (), + m_value_object_list () +{ + if (sc_ptr != NULL) + m_sc = *sc_ptr; +} + +StackFrame::StackFrame (lldb::user_id_t frame_idx, Thread &thread, RegisterContextSP ®_context_sp, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr) : + UserID (frame_idx), + m_thread (thread), + m_reg_context_sp(reg_context_sp), + m_id(cfa), + m_pc(NULL, pc), + m_sc(), + m_flags(), + m_frame_base(), + m_frame_base_error(), + m_variable_list_sp (), + m_value_object_list () +{ + if (sc_ptr != NULL) + m_sc = *sc_ptr; +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StackFrame::~StackFrame() +{ +} + +StackID& +StackFrame::GetStackID() +{ + // Make sure we have resolved our stack ID's address range before we give + // it out to any external clients + if (m_id.GetStartAddress().IsValid() == 0 && m_flags.IsClear(RESOLVED_FRAME_ID)) + { + m_flags.Set (RESOLVED_FRAME_ID); + + // Resolve our PC to section offset if we haven't alreday done so + // and if we don't have a module. The resolved address section will + // contain the module to which it belongs. + if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_PC_SO_ADDR)) + GetPC(); + + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction; + + if (m_sc.module_sp) + { + if (m_sc.module_sp->ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextFunction) + { + assert (m_sc.function); + m_id.SetStartAddress(m_sc.function->GetAddressRange().GetBaseAddress()); + } + else if (m_sc.module_sp->ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextSymbol) + { + assert (m_sc.symbol); + AddressRange *symbol_range_ptr = m_sc.symbol->GetAddressRangePtr(); + if (symbol_range_ptr) + m_id.SetStartAddress(symbol_range_ptr->GetBaseAddress()); + } + } +// else if (m_sc.target != NULL) +// { +// if (m_sc.target->GetImages().ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextFunction) +// { +// assert (m_sc.function); +// m_id.GetAddressRange() = m_sc.function->GetAddressRange(); +// } +// else if (m_sc.target->GetImages().ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextSymbol) +// { +// assert (m_sc.symbol); +// AddressRange *symbol_range_ptr = m_sc.symbol->GetAddressRange(); +// if (symbol_range_ptr) +// m_id.GetAddressRange() = *symbol_range_ptr; +// } +// } + } + return m_id; +} + +Address& +StackFrame::GetPC() +{ + if (m_flags.IsClear(RESOLVED_PC_SO_ADDR) && !m_pc.IsSectionOffset()) + { + m_flags.Set (RESOLVED_PC_SO_ADDR); + + // Resolve the PC into a temporary address because if ResolveLoadAddress + // fails to resolve the address, it will clear the address object... + Address resolved_pc; + if (m_thread.GetProcess().ResolveLoadAddress(m_pc.GetOffset(), resolved_pc)) + { + m_pc = resolved_pc; + const Section *section = m_pc.GetSection(); + if (section) + { + Module *module = section->GetModule(); + if (module) + { + m_sc.module_sp = module->GetSP(); + if (m_sc.module_sp) + m_flags.Set(eSymbolContextModule); + } + } + } + } + return m_pc; +} + +void +StackFrame::ChangePC (addr_t pc) +{ + m_pc.SetOffset(pc); + m_pc.SetSection(NULL); + m_sc.Clear(); + m_flags.SetAllFlagBits(0); + m_thread.ClearStackFrames (); +} + +const char * +StackFrame::Disassemble () +{ + if (m_disassembly.GetSize() == 0) + { + ExecutionContext exe_ctx; + Calculate(exe_ctx); + Disassembler::Disassemble (m_thread.GetProcess().GetTarget().GetArchitecture(), + exe_ctx, + 0, + m_disassembly); + if (m_disassembly.GetSize() == 0) + return NULL; + } + return m_disassembly.GetData(); +} + +//---------------------------------------------------------------------- +// Get the symbol context if we already haven't done so by resolving the +// PC address as much as possible. This way when we pass around a +// StackFrame object, everyone will have as much information as +// possible and no one will ever have to look things up manually. +//---------------------------------------------------------------------- +const SymbolContext& +StackFrame::GetSymbolContext (uint32_t resolve_scope) +{ + // Copy our internal symbol context into "sc". + + if ((m_flags.GetAllFlagBits() & resolve_scope) != resolve_scope) + { + // Resolve our PC to section offset if we haven't alreday done so + // and if we don't have a module. The resolved address section will + // contain the module to which it belongs + if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_PC_SO_ADDR)) + GetPC(); + + // If this is not frame zero, then we need to subtract 1 from the PC + // value when doing address lookups since the PC will be on the + // instruction following the function call instruction... + + Address lookup_addr(GetPC()); + if (GetID() > 0 && lookup_addr.IsValid()) + { + addr_t offset = lookup_addr.GetOffset(); + if (offset > 0) + lookup_addr.SetOffset(offset - 1); + } + + if (m_sc.module_sp) + { + // We have something in our stack frame symbol context, lets check + // if we haven't already tried to lookup one of those things. If we + // haven't then we will do the query. + if ((m_sc.comp_unit == NULL && (resolve_scope & eSymbolContextCompUnit ) && m_flags.IsClear(eSymbolContextCompUnit )) || + (m_sc.function == NULL && (resolve_scope & eSymbolContextFunction ) && m_flags.IsClear(eSymbolContextFunction )) || + (m_sc.block == NULL && (resolve_scope & eSymbolContextBlock ) && m_flags.IsClear(eSymbolContextBlock )) || + (m_sc.symbol == NULL && (resolve_scope & eSymbolContextSymbol ) && m_flags.IsClear(eSymbolContextSymbol )) || + (!m_sc.line_entry.IsValid() && (resolve_scope & eSymbolContextLineEntry) && m_flags.IsClear(eSymbolContextLineEntry ))) + { + // We might be resolving less information than what is already + // in our current symbol context so resolve into a temporary + // symbol context "sc" so we don't clear out data we have + // already found in "m_sc" + SymbolContext sc; + // Set flags that indicate what we have tried to resolve + const uint32_t resolved = m_sc.module_sp->ResolveSymbolContextForAddress (lookup_addr, resolve_scope, sc); + if (resolved & eSymbolContextCompUnit) m_sc.comp_unit = sc.comp_unit; + if (resolved & eSymbolContextFunction) m_sc.function = sc.function; + if (resolved & eSymbolContextBlock) m_sc.block = sc.block; + if (resolved & eSymbolContextSymbol) m_sc.symbol = sc.symbol; + if (resolved & eSymbolContextLineEntry) m_sc.line_entry = sc.line_entry; + } + } + else + { + // If we don't have a module, then we can't have the compile unit, + // function, block, line entry or symbol, so we can safely call + // ResolveSymbolContextForAddress with our symbol context member m_sc. + m_thread.GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (lookup_addr, resolve_scope, m_sc); + } + + // If the target was requested add that: + if (m_sc.target_sp.get() == NULL) + m_sc.target_sp = CalculateProcess()->GetTarget().GetSP(); + + // Update our internal flags so we remember what we have tried to locate so + // we don't have to keep trying when more calls to this function are made. + m_flags.Set(resolve_scope); + } + + // Return the symbol context with everything that was possible to resolve + // resolved. + return m_sc; +} + + +VariableList * +StackFrame::GetVariableList () +{ + if (m_flags.IsClear(RESOLVED_VARIABLES)) + { + m_flags.Set(RESOLVED_VARIABLES); + + GetSymbolContext(eSymbolContextFunction); + if (m_sc.function) + { + bool get_child_variables = true; + bool can_create = true; + m_variable_list_sp = m_sc.function->GetBlocks(can_create).GetVariableList (Block::RootID, get_child_variables, can_create); + } + } + return m_variable_list_sp.get(); +} + + +bool +StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr) +{ + if (m_flags.IsClear(GOT_FRAME_BASE)) + { + if (m_sc.function) + { + m_frame_base.Clear(); + m_frame_base_error.Clear(); + + m_flags.Set(GOT_FRAME_BASE); + ExecutionContext exe_ctx (&m_thread.GetProcess(), &m_thread, this); + Value expr_value; + if (m_sc.function->GetFrameBaseExpression().Evaluate(&exe_ctx, NULL, NULL, expr_value, &m_frame_base_error) < 0) + { + // We should really have an error if evaluate returns, but in case + // we don't, lets set the error to something at least. + if (m_frame_base_error.Success()) + m_frame_base_error.SetErrorString("Evaluation of the frame base expression failed."); + } + else + { + m_frame_base = expr_value.ResolveValue(&exe_ctx, NULL); + } + } + else + { + m_frame_base_error.SetErrorString ("No function in symbol context."); + } + } + + if (m_frame_base_error.Success()) + frame_base = m_frame_base; + + if (error_ptr) + *error_ptr = m_frame_base_error; + return m_frame_base_error.Success(); +} + +RegisterContext * +StackFrame::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp.reset (m_thread.CreateRegisterContextForFrame (this)); + return m_reg_context_sp.get(); +} + +bool +StackFrame::HasDebugInformation () +{ + GetSymbolContext(eSymbolContextLineEntry); + return m_sc.line_entry.IsValid(); +} + +ValueObjectList & +StackFrame::GetValueObjectList() +{ + return m_value_object_list; +} + + +Target * +StackFrame::CalculateTarget () +{ + return m_thread.CalculateTarget(); +} + +Process * +StackFrame::CalculateProcess () +{ + return m_thread.CalculateProcess(); +} + +Thread * +StackFrame::CalculateThread () +{ + return &m_thread; +} + +StackFrame * +StackFrame::CalculateStackFrame () +{ + return this; +} + + +void +StackFrame::Calculate (ExecutionContext &exe_ctx) +{ + m_thread.Calculate (exe_ctx); + exe_ctx.frame = this; +} + +void +StackFrame::Dump (Stream *strm, bool show_frame_index) +{ + if (strm == NULL) + return; + + if (show_frame_index) + strm->Printf("frame #%u: ", GetID()); + strm->Printf("pc = 0x%0*llx", m_thread.GetProcess().GetAddressByteSize() * 2, GetRegisterContext()->GetPC()); + SymbolContext sc (GetSymbolContext(eSymbolContextEverything)); + strm->PutCString(", where = "); + sc.DumpStopContext(strm, &m_thread.GetProcess(), GetPC()); +} + diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp new file mode 100644 index 000000000000..8615f7146739 --- /dev/null +++ b/lldb/source/Target/StackFrameList.cpp @@ -0,0 +1,135 @@ +//===-- StackFrameList.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/Target/StackFrameList.h" +#include "lldb/Target/StackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StackFrameList constructor +//---------------------------------------------------------------------- +StackFrameList::StackFrameList() : + m_mutex (Mutex::eMutexTypeRecursive), + m_frames (), + m_current_frame_idx (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StackFrameList::~StackFrameList() +{ +} + + +uint32_t +StackFrameList::GetNumFrames() const +{ + Mutex::Locker locker (m_mutex); + return m_frames.size(); +} + +// After we have determined the number of frames, we can set the count here +// and have the frame info be generated on demand. +void +StackFrameList::SetNumFrames(uint32_t count) +{ + Mutex::Locker locker (m_mutex); + return m_frames.resize(count); +} + +StackFrameSP +StackFrameList::GetFrameAtIndex (uint32_t idx) const +{ + StackFrameSP frame_sp; + { + Mutex::Locker locker (m_mutex); + if (idx < m_frames.size()) + frame_sp = m_frames[idx]; + } + return frame_sp; +} + +bool +StackFrameList::SetFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp) +{ + Mutex::Locker locker (m_mutex); + if (idx >= m_frames.size()) + m_frames.resize(idx + 1); + // Make sure allocation succeeded by checking bounds again + if (idx < m_frames.size()) + { + m_frames[idx] = frame_sp; + return true; + } + return false; // resize failed, out of memory? +} + +uint32_t +StackFrameList::GetCurrentFrameIndex () const +{ + Mutex::Locker locker (m_mutex); + return m_current_frame_idx; +} + + +uint32_t +StackFrameList::SetCurrentFrame (lldb_private::StackFrame *frame) +{ + Mutex::Locker locker (m_mutex); + const_iterator pos, + begin = m_frames.begin(), + end = m_frames.end(); + for (pos = begin; pos != end; ++pos) + { + if (pos->get() == frame) + { + m_current_frame_idx = std::distance (begin, pos); + return m_current_frame_idx; + } + } + m_current_frame_idx = 0; + return m_current_frame_idx; +} + +// Mark a stack frame as the current frame using the frame index +void +StackFrameList::SetCurrentFrameByIndex (uint32_t idx) +{ + Mutex::Locker locker (m_mutex); + m_current_frame_idx = idx; +} + +// The thread has been run, reset the number stack frames to zero so we can +// determine how many frames we have lazily. +void +StackFrameList::Clear () +{ + Mutex::Locker locker (m_mutex); + m_frames.clear(); +} + +void +StackFrameList::InvalidateFrames (uint32_t start_idx) +{ + Mutex::Locker locker (m_mutex); + size_t num_frames = m_frames.size(); + while (start_idx < num_frames) + { + m_frames[start_idx].reset(); + ++start_idx; + } +} diff --git a/lldb/source/Target/StackID.cpp b/lldb/source/Target/StackID.cpp new file mode 100644 index 000000000000..6f903aed6da6 --- /dev/null +++ b/lldb/source/Target/StackID.cpp @@ -0,0 +1,110 @@ +//===-- StackID.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/Target/StackID.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StackID constructor +//---------------------------------------------------------------------- +StackID::StackID() : + m_start_address(), + m_cfa() +{ +} + +//---------------------------------------------------------------------- +// StackID constructor with args +//---------------------------------------------------------------------- +StackID::StackID (const Address& start_address, lldb::addr_t cfa) : + m_start_address (start_address), + m_cfa (cfa) +{ +} + +StackID::StackID (lldb::addr_t cfa) : + m_start_address (), + m_cfa (cfa) +{ +} + +//---------------------------------------------------------------------- +// StackID copy constructor +//---------------------------------------------------------------------- +StackID::StackID(const StackID& rhs) : + m_start_address (rhs.m_start_address), + m_cfa (rhs.m_cfa) +{ +} + +//---------------------------------------------------------------------- +// StackID assignment operator +//---------------------------------------------------------------------- +const StackID& +StackID::operator=(const StackID& rhs) +{ + if (this != &rhs) + { + m_start_address = rhs.m_start_address; + m_cfa = rhs.m_cfa; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StackID::~StackID() +{ +} + + +const Address& +StackID::GetStartAddress() const +{ + return m_start_address; +} + +void +StackID::SetStartAddress(const Address& start_address) +{ + m_start_address = start_address; +} + +lldb::addr_t +StackID::GetCallFrameAddress() const +{ + return m_cfa; +} + + +bool +lldb_private::operator== (const StackID& lhs, const StackID& rhs) +{ + return lhs.GetCallFrameAddress() == rhs.GetCallFrameAddress() && lhs.GetStartAddress() == rhs.GetStartAddress(); +} + +bool +lldb_private::operator!= (const StackID& lhs, const StackID& rhs) +{ + return lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress() || lhs.GetStartAddress() != rhs.GetStartAddress(); +} + +bool +lldb_private::operator< (const StackID& lhs, const StackID& rhs) +{ + return lhs.GetCallFrameAddress() < rhs.GetCallFrameAddress(); +} + diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp new file mode 100644 index 000000000000..48f2ea70fd04 --- /dev/null +++ b/lldb/source/Target/Target.cpp @@ -0,0 +1,707 @@ +//===-- Target.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/Target/Target.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverAddress.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Breakpoint/BreakpointResolverName.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/Debugger.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Target constructor +//---------------------------------------------------------------------- +Target::Target() : + Broadcaster("Target"), + m_images(), + m_breakpoint_list (false), + m_internal_breakpoint_list (true), + m_process_sp(), + m_triple(), + m_search_filter_sp(), + m_image_search_paths (ImageSearchPathsChanged, this), + m_scratch_ast_context_ap(NULL) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Target::Target()", this); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Target::~Target() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Target::~Target()", this); + DeleteCurrentProcess (); +} + +void +Target::Dump (Stream *s) +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->PutCString("Target\n"); + s->IndentMore(); + m_images.Dump(s); + m_breakpoint_list.Dump(s); + m_internal_breakpoint_list.Dump(s); +// if (m_process_sp.get()) +// m_process_sp->Dump(s); + s->IndentLess(); +} + +void +Target::DeleteCurrentProcess () +{ + if (m_process_sp.get()) + { + if (m_process_sp->IsAlive()) + m_process_sp->Destroy(); + else + m_process_sp->Finalize(); + + // Do any cleanup of the target we need to do between process instances. + // NB It is better to do this before destroying the process in case the + // clean up needs some help from the process. + m_breakpoint_list.ClearAllBreakpointSites(); + m_internal_breakpoint_list.ClearAllBreakpointSites(); + m_process_sp.reset(); + } +} + +const lldb::ProcessSP & +Target::CreateProcess (Listener &listener, const char *plugin_name) +{ + DeleteCurrentProcess (); + m_process_sp.reset(Process::FindPlugin(*this, plugin_name, listener)); + return m_process_sp; +} + +const lldb::ProcessSP & +Target::GetProcessSP () const +{ + return m_process_sp; +} + +lldb::TargetSP +Target::GetSP() +{ + return Debugger::GetSharedInstance().GetTargetList().GetTargetSP(this); +} + +BreakpointList & +Target::GetBreakpointList(bool internal) +{ + if (internal) + return m_internal_breakpoint_list; + else + return m_breakpoint_list; +} + +const BreakpointList & +Target::GetBreakpointList(bool internal) const +{ + if (internal) + return m_internal_breakpoint_list; + else + return m_breakpoint_list; +} + +BreakpointSP +Target::GetBreakpointByID (break_id_t break_id) +{ + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); + + return bp_sp; +} + +BreakpointSP +Target::CreateBreakpoint (const FileSpec *containingModule, const FileSpec &file, uint32_t line_no, bool check_inlines, bool internal) +{ + SearchFilterSP filter_sp(GetSearchFilterForModule (containingModule)); + BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine (NULL, file, line_no, check_inlines)); + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + + +BreakpointSP +Target::CreateBreakpoint (lldb::addr_t load_addr, bool internal) +{ + BreakpointSP bp_sp; + Address so_addr; + // Attempt to resolve our load address if possible, though it is ok if + // it doesn't resolve to section/offset. + + Process *process = GetProcessSP().get(); + if (process && process->ResolveLoadAddress(load_addr, so_addr)) + bp_sp = CreateBreakpoint(so_addr, internal); + return bp_sp; +} + +BreakpointSP +Target::CreateBreakpoint (Address &addr, bool internal) +{ + TargetSP target_sp = this->GetSP(); + SearchFilterSP filter_sp(new SearchFilter (target_sp)); + BreakpointResolverSP resolver_sp (new BreakpointResolverAddress (NULL, addr)); + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + +BreakpointSP +Target::CreateBreakpoint (FileSpec *containingModule, const char *func_name, bool internal) +{ + SearchFilterSP filter_sp(GetSearchFilterForModule (containingModule)); + BreakpointResolverSP resolver_sp (new BreakpointResolverName (NULL, func_name)); + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + + +SearchFilterSP +Target::GetSearchFilterForModule (const FileSpec *containingModule) +{ + SearchFilterSP filter_sp; + lldb::TargetSP target_sp = this->GetSP(); + if (containingModule != NULL) + { + // TODO: We should look into sharing module based search filters + // across many breakpoints like we do for the simple target based one + filter_sp.reset (new SearchFilterByModule (target_sp, *containingModule)); + } + else + { + if (m_search_filter_sp.get() == NULL) + m_search_filter_sp.reset (new SearchFilter (target_sp)); + filter_sp = m_search_filter_sp; + } + return filter_sp; +} + +BreakpointSP +Target::CreateBreakpoint (FileSpec *containingModule, RegularExpression &func_regex, bool internal) +{ + SearchFilterSP filter_sp(GetSearchFilterForModule (containingModule)); + BreakpointResolverSP resolver_sp(new BreakpointResolverName (NULL, func_regex)); + + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + +BreakpointSP +Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal) +{ + BreakpointSP bp_sp; + if (filter_sp && resolver_sp) + { + bp_sp.reset(new Breakpoint (*this, filter_sp, resolver_sp)); + resolver_sp->SetBreakpoint (bp_sp.get()); + + if (internal) + m_internal_breakpoint_list.Add (bp_sp); + else + m_breakpoint_list.Add (bp_sp); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + { + StreamString s; + bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, internal ? "yes" : "no", s.GetData()); + } + + // Broadcast the breakpoint creation event. + if (!internal && EventTypeHasListeners(eBroadcastBitBreakpointChanged)) + { + BroadcastEvent (eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (Breakpoint::BreakpointEventData::eBreakpointAdded, bp_sp)); + } + + bp_sp->ResolveBreakpoint(); + } + return bp_sp; +} + +void +Target::RemoveAllBreakpoints (bool internal_also) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); + + m_breakpoint_list.RemoveAll(); + if (internal_also) + m_internal_breakpoint_list.RemoveAll(); +} + +void +Target::DisableAllBreakpoints (bool internal_also) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); + + m_breakpoint_list.SetEnabledAll (false); + if (internal_also) + m_internal_breakpoint_list.SetEnabledAll (false); +} + +void +Target::EnableAllBreakpoints (bool internal_also) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); + + m_breakpoint_list.SetEnabledAll (true); + if (internal_also) + m_internal_breakpoint_list.SetEnabledAll (true); +} + +bool +Target::RemoveBreakpointByID (break_id_t break_id) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); + + if (DisableBreakpointByID (break_id)) + { + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + m_internal_breakpoint_list.Remove(break_id); + else + m_breakpoint_list.Remove(break_id); + return true; + } + return false; +} + +bool +Target::DisableBreakpointByID (break_id_t break_id) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); + + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); + if (bp_sp) + { + bp_sp->SetEnabled (false); + return true; + } + return false; +} + +bool +Target::EnableBreakpointByID (break_id_t break_id) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (break_id = %i, internal = %s)\n", + __FUNCTION__, + break_id, + LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); + + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); + + if (bp_sp) + { + bp_sp->SetEnabled (true); + return true; + } + return false; +} + +ModuleSP +Target::GetExecutableModule () +{ + ModuleSP executable_sp; + if (m_images.GetSize() > 0) + executable_sp = m_images.GetModuleAtIndex(0); + return executable_sp; +} + +void +Target::SetExecutableModule (ModuleSP& executable_sp, bool get_dependent_files) +{ + m_images.Clear(); + m_scratch_ast_context_ap.reset(); + + if (executable_sp.get()) + { + Timer scoped_timer (__PRETTY_FUNCTION__, + "Target::SetExecutableModule (executable = '%s/%s')", + executable_sp->GetFileSpec().GetDirectory().AsCString(), + executable_sp->GetFileSpec().GetFilename().AsCString()); + + m_images.Append(executable_sp); // The first image is our exectuable file + + ArchSpec exe_arch = executable_sp->GetArchitecture(); + FileSpecList dependent_files; + ObjectFile * executable_objfile = executable_sp->GetObjectFile(); + if (executable_objfile == NULL) + { + + FileSpec bundle_executable(executable_sp->GetFileSpec()); + if (Host::ResolveExecutableInBundle (&bundle_executable)) + { + ModuleSP bundle_exe_module_sp(GetSharedModule(bundle_executable, + exe_arch)); + SetExecutableModule (bundle_exe_module_sp, get_dependent_files); + if (bundle_exe_module_sp->GetObjectFile() != NULL) + executable_sp = bundle_exe_module_sp; + return; + } + } + + if (executable_objfile) + { + executable_objfile->GetDependentModules(dependent_files); + for (uint32_t i=0; iDump(&s);// REMOVE THIS, DEBUG ONLY + ObjectFile *objfile = image_module_sp->GetObjectFile(); + if (objfile) + objfile->GetDependentModules(dependent_files); + } + } + } + + // Now see if we know the target triple, and if so, create our scratch AST context: + ConstString target_triple; + if (GetTargetTriple(target_triple)) + { + m_scratch_ast_context_ap.reset (new ClangASTContext(target_triple.GetCString())); + } + } +} + + +ModuleList& +Target::GetImages () +{ + return m_images; +} + +ArchSpec +Target::GetArchitecture () const +{ + ArchSpec arch; + if (m_images.GetSize() > 0) + { + Module *exe_module = m_images.GetModulePointerAtIndex(0); + if (exe_module) + arch = exe_module->GetArchitecture(); + } + return arch; +} + + + +bool +Target::GetTargetTriple(ConstString &triple) +{ + triple.Clear(); + + if (m_triple) + { + triple = m_triple; + } + else + { + Module *exe_module = GetExecutableModule().get(); + if (exe_module) + { + ObjectFile *objfile = exe_module->GetObjectFile(); + if (objfile) + { + objfile->GetTargetTriple(m_triple); + triple = m_triple; + } + } + } + return !triple.IsEmpty(); +} + +void +Target::ModuleAdded (ModuleSP &module_sp) +{ + // A module is being added to this target for the first time + ModuleList module_list; + module_list.Append(module_sp); + ModulesDidLoad (module_list); +} + +void +Target::ModuleUpdated (ModuleSP &old_module_sp, ModuleSP &new_module_sp) +{ + // A module is being added to this target for the first time + ModuleList module_list; + module_list.Append (old_module_sp); + ModulesDidUnload (module_list); + module_list.Clear (); + module_list.Append (new_module_sp); + ModulesDidLoad (module_list); +} + +void +Target::ModulesDidLoad (ModuleList &module_list) +{ + m_breakpoint_list.UpdateBreakpoints (module_list, true); + // TODO: make event data that packages up the module_list + BroadcastEvent (eBroadcastBitModulesLoaded, NULL); +} + +void +Target::ModulesDidUnload (ModuleList &module_list) +{ + m_breakpoint_list.UpdateBreakpoints (module_list, false); + // TODO: make event data that packages up the module_list + BroadcastEvent (eBroadcastBitModulesUnloaded, NULL); +} + +size_t +Target::ReadMemory +( + lldb::AddressType addr_type, + lldb::addr_t addr, + void *dst, + size_t dst_len, + Error &error, + ObjectFile* objfile +) +{ + size_t bytes_read = 0; + error.Clear(); + switch (addr_type) + { + case eAddressTypeFile: + if (objfile) + { + if (m_process_sp.get()) + { + // If we have an execution context with a process, lets try and + // resolve the file address in "objfile" and read it from the + // process + Address so_addr(addr, objfile->GetSectionList()); + lldb::addr_t load_addr = so_addr.GetLoadAddress(m_process_sp.get()); + if (load_addr == LLDB_INVALID_ADDRESS) + { + if (objfile->GetFileSpec()) + error.SetErrorStringWithFormat("0x%llx can't be resolved, %s in not currently loaded.\n", addr, objfile->GetFileSpec().GetFilename().AsCString()); + else + error.SetErrorStringWithFormat("0x%llx can't be resolved.\n", addr, objfile->GetFileSpec().GetFilename().AsCString()); + } + else + { + bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error); + if (bytes_read != dst_len) + { + if (error.Success()) + { + if (bytes_read == 0) + error.SetErrorStringWithFormat("Read memory from 0x%llx failed.\n", load_addr); + else + error.SetErrorStringWithFormat("Only %zu of %zu bytes were read from memory at 0x%llx.\n", bytes_read, dst_len, load_addr); + } + } + } + } + else + { + // Try and read the file based address from the object file's + // section data. + } + } + break; + + case eAddressTypeLoad: + if (m_process_sp.get()) + { + bytes_read = m_process_sp->ReadMemory(addr, dst, dst_len, error); + if (bytes_read != dst_len) + { + if (error.Success()) + { + if (bytes_read == 0) + error.SetErrorStringWithFormat("Read memory from 0x%llx failed.\n", addr); + else + error.SetErrorStringWithFormat("Only %zu of %zu bytes were read from memory at 0x%llx.\n", bytes_read, dst_len, addr); + } + } + } + else + error.SetErrorStringWithFormat("Need valid process to read load address.\n"); + break; + + case eAddressTypeHost: + // The address is an address in this process, so just copy it + ::memcpy (dst, (uint8_t*)NULL + addr, dst_len); + break; + + default: + error.SetErrorStringWithFormat ("Unsupported lldb::AddressType value (%i).\n", addr_type); + break; + } + return bytes_read; +} + + +ModuleSP +Target::GetSharedModule +( + const FileSpec& file_spec, + const ArchSpec& arch, + const UUID *uuid_ptr, + const ConstString *object_name, + off_t object_offset, + Error *error_ptr +) +{ + // Don't pass in the UUID so we can tell if we have a stale value in our list + ModuleSP old_module_sp; // This will get filled in if we have a new version of the library + bool did_create_module = false; + ModuleSP module_sp; + + // If there are image search path entries, try to use them first to acquire a suitable image. + + Error error; + + if (m_image_search_paths.GetSize()) + { + FileSpec transformed_spec; + if (m_image_search_paths.RemapPath (file_spec.GetDirectory(), transformed_spec.GetDirectory())) + { + transformed_spec.GetFilename() = file_spec.GetFilename(); + error = ModuleList::GetSharedModule (transformed_spec, arch, uuid_ptr, object_name, object_offset, module_sp, &old_module_sp, &did_create_module); + } + } + + // If a module hasn't been found yet, use the unmodified path. + + if (!module_sp) + { + error = (ModuleList::GetSharedModule (file_spec, arch, uuid_ptr, object_name, object_offset, module_sp, &old_module_sp, &did_create_module)); + } + + if (module_sp) + { + m_images.Append (module_sp); + if (did_create_module) + { + if (old_module_sp && m_images.GetIndexForModule (old_module_sp.get()) != LLDB_INVALID_INDEX32) + ModuleUpdated(old_module_sp, module_sp); + else + ModuleAdded(module_sp); + } + } + if (error_ptr) + *error_ptr = error; + return module_sp; +} + + +Target * +Target::CalculateTarget () +{ + return this; +} + +Process * +Target::CalculateProcess () +{ + return NULL; +} + +Thread * +Target::CalculateThread () +{ + return NULL; +} + +StackFrame * +Target::CalculateStackFrame () +{ + return NULL; +} + +void +Target::Calculate (ExecutionContext &exe_ctx) +{ + exe_ctx.target = this; + exe_ctx.process = NULL; // Do NOT fill in process... + exe_ctx.thread = NULL; + exe_ctx.frame = NULL; +} + +PathMappingList & +Target::GetImageSearchPathList () +{ + return m_image_search_paths; +} + +void +Target::ImageSearchPathsChanged +( + const PathMappingList &path_list, + void *baton +) +{ + Target *target = (Target *)baton; + if (target->m_images.GetSize() > 1) + { + ModuleSP exe_module_sp (target->GetExecutableModule()); + if (exe_module_sp) + { + target->m_images.Clear(); + target->SetExecutableModule (exe_module_sp, true); + } + } +} + +ClangASTContext * +Target::GetScratchClangASTContext() +{ + return m_scratch_ast_context_ap.get(); +} diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp new file mode 100644 index 000000000000..d92940003d6e --- /dev/null +++ b/lldb/source/Target/TargetList.cpp @@ -0,0 +1,342 @@ +//===-- TargetList.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/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// TargetList constructor +//---------------------------------------------------------------------- +TargetList::TargetList() : + Broadcaster("TargetList"), + m_target_list(), + m_target_list_mutex (Mutex::eMutexTypeRecursive), + m_current_target_idx (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TargetList::~TargetList() +{ + Mutex::Locker locker(m_target_list_mutex); + m_target_list.clear(); +} + +Error +TargetList::CreateTarget +( + const FileSpec& file, + const ArchSpec& arch, + const UUID *uuid_ptr, + bool get_dependent_files, + TargetSP &target_sp +) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "TargetList::CreateTarget (file = '%s/%s', arch = '%s', uuid = %p)", + file.GetDirectory().AsCString(), + file.GetFilename().AsCString(), + arch.AsCString(), + uuid_ptr); + ModuleSP exe_module_sp; + FileSpec resolved_file(file); + if (!Host::ResolveExecutableInBundle (&resolved_file)) + resolved_file = file; + + Error error = ModuleList::GetSharedModule(resolved_file, + arch, + uuid_ptr, + NULL, + 0, + exe_module_sp, + NULL, + NULL); + if (exe_module_sp) + { + target_sp.reset(new Target); + target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); + + if (target_sp.get()) + { + Mutex::Locker locker(m_target_list_mutex); + m_current_target_idx = m_target_list.size(); + m_target_list.push_back(target_sp); + } + +// target_sp.reset(new Target); +// // Let the target resolve any funky bundle paths before we try and get +// // the object file... +// target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); +// if (exe_module_sp->GetObjectFile() == NULL) +// { +// error.SetErrorStringWithFormat("%s%s%s: doesn't contain architecture %s", +// file.GetDirectory().AsCString(), +// file.GetDirectory() ? "/" : "", +// file.GetFilename().AsCString(), +// arch.AsCString()); +// } +// else +// { +// if (target_sp.get()) +// { +// error.Clear(); +// Mutex::Locker locker(m_target_list_mutex); +// m_current_target_idx = m_target_list.size(); +// m_target_list.push_back(target_sp); +// } +// } + } + else + { + target_sp.reset(); + } + + return error; +} + +bool +TargetList::DeleteTarget (TargetSP &target_sp) +{ + Mutex::Locker locker(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + + for (pos = m_target_list.begin(); pos != end; ++pos) + { + if (pos->get() == target_sp.get()) + { + m_target_list.erase(pos); + return true; + } + } + return false; +} + + +TargetSP +TargetList::FindTargetWithExecutableAndArchitecture +( + const FileSpec &exe_file_spec, + const ArchSpec *exe_arch_ptr +) const +{ + Mutex::Locker locker (m_target_list_mutex); + TargetSP target_sp; + bool full_match = exe_file_spec.GetDirectory(); + + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + ModuleSP module_sp ((*pos)->GetExecutableModule()); + + if (module_sp) + { + if (FileSpec::Equal (exe_file_spec, module_sp->GetFileSpec(), full_match)) + { + if (exe_arch_ptr) + { + if (*exe_arch_ptr != module_sp->GetArchitecture()) + continue; + } + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP +TargetList::FindTargetWithProcessID (lldb::pid_t pid) const +{ + Mutex::Locker locker(m_target_list_mutex); + TargetSP target_sp; + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + Process* process = (*pos)->GetProcessSP().get(); + if (process && process->GetID() == pid) + { + target_sp = *pos; + break; + } + } + return target_sp; +} + + +TargetSP +TargetList::FindTargetWithProcess (Process *process) const +{ + TargetSP target_sp; + if (process) + { + Mutex::Locker locker(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + if (process == (*pos)->GetProcessSP().get()) + { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP +TargetList::GetTargetSP (Target *target) const +{ + TargetSP target_sp; + if (target) + { + Mutex::Locker locker(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + if (target == (*pos).get()) + { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +uint32_t +TargetList::SendAsyncInterrupt (lldb::pid_t pid) +{ + uint32_t num_async_interrupts_sent = 0; + + if (pid != LLDB_INVALID_PROCESS_ID) + { + TargetSP target_sp(FindTargetWithProcessID (pid)); + if (target_sp.get()) + { + Process* process = target_sp->GetProcessSP().get(); + if (process) + { + process->BroadcastEvent (Process::eBroadcastBitInterrupt, NULL); + ++num_async_interrupts_sent; + } + } + } + else + { + // We don't have a valid pid to broadcast to, so broadcast to the target + // list's async broadcaster... + BroadcastEvent (Process::eBroadcastBitInterrupt, NULL); + } + + return num_async_interrupts_sent; +} + +uint32_t +TargetList::SignalIfRunning (lldb::pid_t pid, int signo) +{ + uint32_t num_signals_sent = 0; + Process *process = NULL; + if (pid == LLDB_INVALID_PROCESS_ID) + { + // Signal all processes with signal + Mutex::Locker locker(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + process = (*pos)->GetProcessSP().get(); + if (process) + { + if (process->IsAlive()) + { + ++num_signals_sent; + process->Signal (signo); + } + } + } + } + else + { + // Signal a specific process with signal + TargetSP target_sp(FindTargetWithProcessID (pid)); + if (target_sp.get()) + { + process = target_sp->GetProcessSP().get(); + if (process) + { + if (process->IsAlive()) + { + ++num_signals_sent; + process->Signal (signo); + } + } + } + } + return num_signals_sent; +} + +int +TargetList::GetNumTargets () const +{ + Mutex::Locker locker (m_target_list_mutex); + return m_target_list.size(); +} + +lldb::TargetSP +TargetList::GetTargetAtIndex (uint32_t idx) const +{ + TargetSP target_sp; + Mutex::Locker locker (m_target_list_mutex); + if (idx < m_target_list.size()) + target_sp = m_target_list[idx]; + return target_sp; +} + +uint32_t +TargetList::SetCurrentTarget (Target* target) +{ + Mutex::Locker locker (m_target_list_mutex); + collection::const_iterator pos, + begin = m_target_list.begin(), + end = m_target_list.end(); + for (pos = begin; pos != end; ++pos) + { + if (pos->get() == target) + { + m_current_target_idx = std::distance (begin, pos); + return m_current_target_idx; + } + } + m_current_target_idx = 0; + return m_current_target_idx; +} + +lldb::TargetSP +TargetList::GetCurrentTarget () +{ + Mutex::Locker locker (m_target_list_mutex); + if (m_current_target_idx >= m_target_list.size()) + m_current_target_idx = 0; + return GetTargetAtIndex (m_current_target_idx); +} diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp new file mode 100644 index 000000000000..2f852cc3eae4 --- /dev/null +++ b/lldb/source/Target/Thread.cpp @@ -0,0 +1,1121 @@ +//===-- Thread.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-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Target/ThreadPlanContinue.h" +#include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepOverBreakpoint.h" +#include "lldb/Target/ThreadPlanStepThrough.h" +#include "lldb/Target/ThreadPlanStepInRange.h" +#include "lldb/Target/ThreadPlanStepOverRange.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepUntil.h" + +using namespace lldb; +using namespace lldb_private; + +Thread::Thread (Process &process, lldb::tid_t tid) : + UserID (tid), + m_index_id (process.GetNextThreadIndexID ()), + m_reg_context_sp (), + m_process (process), + m_state (eStateUnloaded), + m_plan_stack (), + m_immediate_plan_stack(), + m_completed_plan_stack(), + m_state_mutex (Mutex::eMutexTypeRecursive), + m_frames (), + m_current_frame_idx (0), + m_resume_signal (LLDB_INVALID_SIGNAL_NUMBER), + m_resume_state (eStateRunning) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Thread::Thread(tid = 0x%4.4x)", this, GetID()); + + QueueFundamentalPlan(true); +} + + +Thread::~Thread() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Thread::~Thread(tid = 0x%4.4x)", this, GetID()); +} + +int +Thread::GetResumeSignal () const +{ + return m_resume_signal; +} + +void +Thread::SetResumeSignal (int signal) +{ + m_resume_signal = signal; +} + +StateType +Thread::GetResumeState () const +{ + return m_resume_state; +} + +void +Thread::SetResumeState (StateType state) +{ + m_resume_state = state; +} + +Thread::StopInfo::StopInfo(Thread *thread) : + m_reason (eStopReasonInvalid), + m_description (), + m_thread (thread), + m_details () +{ + m_description[0] = '\0'; +} + +Thread::StopInfo::~StopInfo() +{ +} + + +void +Thread::StopInfo::Clear() +{ + m_reason = eStopReasonInvalid; + m_completed_plan_sp.reset(); + m_description[0] = '\0'; + ::bzero (&m_details, sizeof(m_details)); +} + +StopReason +Thread::StopInfo::GetStopReason() const +{ + return m_reason; +} + +const char * +Thread::StopInfo::GetStopDescription() const +{ + if (m_description[0]) + return m_description; + return NULL; +} + +void +Thread::StopInfo::SetStopDescription(const char *desc) +{ + if (desc && desc[0]) + { + ::snprintf (m_description, sizeof(m_description), "%s", desc); + } + else + { + m_description[0] = '\0'; + } +} + +void +Thread::StopInfo::SetThread (Thread* thread) +{ + m_thread = thread; +} + +Thread * +Thread::StopInfo::GetThread () +{ + return m_thread; +} + +lldb::user_id_t +Thread::StopInfo::GetBreakpointSiteID() const +{ + if (m_reason == eStopReasonBreakpoint) + return m_details.breakpoint.bp_site_id; + return LLDB_INVALID_BREAK_ID; +} + +void +Thread::StopInfo::SetStopReasonWithBreakpointSiteID (lldb::user_id_t bp_site_id) +{ + m_reason = eStopReasonBreakpoint; + m_details.breakpoint.bp_site_id = bp_site_id; +} + +lldb::user_id_t +Thread::StopInfo::GetWatchpointID() const +{ + if (m_reason == eStopReasonWatchpoint) + return m_details.watchpoint.watch_id; + return LLDB_INVALID_WATCH_ID; +} + +void +Thread::StopInfo::SetStopReasonWithWatchpointID (lldb::user_id_t watch_id) +{ + m_reason = eStopReasonWatchpoint; + m_details.watchpoint.watch_id = watch_id; +} + + +int +Thread::StopInfo::GetSignal() const +{ + if (m_reason == eStopReasonSignal) + return m_details.signal.signo; + return 0; +} + +lldb::user_id_t +Thread::StopInfo::GetPlanID() const +{ + if (m_reason == eStopReasonPlanComplete) + return m_completed_plan_sp->GetID(); + return LLDB_INVALID_UID; +} + +void +Thread::StopInfo::SetStopReasonWithSignal (int signo) +{ + m_reason = eStopReasonSignal; + m_details.signal.signo = signo; +} + +void +Thread::StopInfo::SetStopReasonToTrace () +{ + m_reason = eStopReasonTrace; +} + +uint32_t +Thread::StopInfo::GetExceptionType() const +{ + if (m_reason == eStopReasonException) + return m_details.exception.type; + return 0; +} + +size_t +Thread::StopInfo::GetExceptionDataCount() const +{ + if (m_reason == eStopReasonException) + return m_details.exception.data_count; + return 0; +} + +void +Thread::StopInfo::SetStopReasonWithException (uint32_t exc_type, size_t exc_data_count) +{ + m_reason = eStopReasonException; + m_details.exception.type = exc_type; + m_details.exception.data_count = exc_data_count; +} + +void +Thread::StopInfo::SetStopReasonWithPlan (ThreadPlanSP &thread_plan_sp) +{ + m_reason = eStopReasonPlanComplete; + m_completed_plan_sp = thread_plan_sp; +} + +void +Thread::StopInfo::SetStopReasonToNone () +{ + Clear(); + m_reason = eStopReasonNone; +} + +lldb::addr_t +Thread::StopInfo::GetExceptionDataAtIndex (uint32_t idx) const +{ + if (m_reason == eStopReasonException && idx < m_details.exception.data_count) + return m_details.exception.data[idx]; + return 0; + +} + + +bool +Thread::StopInfo::SetExceptionDataAtIndex (uint32_t idx, lldb::addr_t data) +{ + if (m_reason == eStopReasonException && idx < m_details.exception.data_count) + { + m_details.exception.data[idx] = data; + return true; + } + return false; +} + +void +Thread::StopInfo::Dump (Stream *s) const +{ + if (m_description[0]) + s->Printf("%s", m_description); + else + { + switch (m_reason) + { + case eStopReasonInvalid: + s->PutCString("invalid"); + break; + + case eStopReasonNone: + s->PutCString("none"); + break; + + case eStopReasonTrace: + s->PutCString("trace"); + break; + + case eStopReasonBreakpoint: + { + bool no_details = true; + s->PutCString ("breakpoint "); + if (m_thread) + { + BreakpointSiteSP bp_site_sp = m_thread->GetProcess().GetBreakpointSiteList().FindByID(m_details.breakpoint.bp_site_id); + if (bp_site_sp) + { + bp_site_sp->GetDescription(s, lldb::eDescriptionLevelBrief); + no_details = false; + } + } + + if (no_details) + s->Printf ("site id: %d", m_details.breakpoint.bp_site_id); + } + break; + + case eStopReasonWatchpoint: + s->Printf("watchpoint (site id = %u)", m_details.watchpoint.watch_id); + break; + + case eStopReasonSignal: + { + s->Printf("signal: signo = %i", m_details.signal.signo); + const char * signal_name = m_thread->GetProcess().GetUnixSignals().GetSignalAsCString (m_details.signal.signo); + if (signal_name) + s->Printf(" (%s)", signal_name); + } + break; + + case eStopReasonException: + { + s->Printf("exception: type = 0x%8.8x, data_count = %zu", m_details.exception.type, m_details.exception.data_count); + uint32_t i; + for (i=0; iPrintf(", data[%u] = 0x%8.8llx", i, m_details.exception.data[i]); + } + } + break; + + case eStopReasonPlanComplete: + { + m_completed_plan_sp->GetDescription (s, lldb::eDescriptionLevelBrief); + } + break; + } + } +} + +bool +Thread::GetStopInfo (Thread::StopInfo *stop_info) +{ + stop_info->SetThread(this); + ThreadPlanSP completed_plan = GetCompletedPlan(); + if (completed_plan != NULL) + { + stop_info->Clear (); + stop_info->SetStopReasonWithPlan (completed_plan); + return true; + } + else + return GetRawStopReason (stop_info); +} + +bool +Thread::ThreadStoppedForAReason (void) +{ + Thread::StopInfo stop_info; + stop_info.SetThread(this); + if (GetRawStopReason (&stop_info)) + { + StopReason reason = stop_info.GetStopReason(); + if (reason == eStopReasonInvalid || reason == eStopReasonNone) + return false; + else + return true; + } + else + return false; +} + +StateType +Thread::GetState() const +{ + // If any other threads access this we will need a mutex for it + Mutex::Locker locker(m_state_mutex); + return m_state; +} + +void +Thread::SetState(StateType state) +{ + Mutex::Locker locker(m_state_mutex); + m_state = state; +} + +void +Thread::WillStop() +{ + ThreadPlan *current_plan = GetCurrentPlan(); + + // FIXME: I may decide to disallow threads with no plans. In which + // case this should go to an assert. + + if (!current_plan) + return; + + current_plan->WillStop(); +} + +void +Thread::SetupForResume () +{ + if (GetResumeState() != eStateSuspended) + { + + // If we're at a breakpoint push the step-over breakpoint plan. Do this before + // telling the current plan it will resume, since we might change what the current + // plan is. + + lldb::addr_t pc = GetRegisterContext()->GetPC(); + BreakpointSiteSP bp_site_sp = GetProcess().GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp && bp_site_sp->IsEnabled()) + { + // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target may not require anything + // special to step over a breakpoint. + + ThreadPlan *cur_plan = GetCurrentPlan(); + ThreadPlanStepOverBreakpoint *step_over_bp = dynamic_cast (cur_plan); + if (step_over_bp == NULL) + { + + ThreadPlanSP step_bp_plan_sp (new ThreadPlanStepOverBreakpoint (*this)); + if (step_bp_plan_sp) + { + if (GetCurrentPlan()->RunState() != eStateStepping) + { + ThreadPlanSP continue_plan_sp (new ThreadPlanContinue(*this, false, eVoteNo, eVoteNoOpinion)); + continue_plan_sp->SetPrivate (true); + QueueThreadPlan (continue_plan_sp, false); + } + step_bp_plan_sp->SetPrivate (true); + QueueThreadPlan (step_bp_plan_sp, false); + } + } + } + } +} + +bool +Thread::WillResume (StateType resume_state) +{ + // At this point clear the completed plan stack. + m_completed_plan_stack.clear(); + m_discarded_plan_stack.clear(); + + // If this thread stopped with a signal, work out what its resume state should + // be. Note if the thread resume state is already set, then don't override it, + // the user must have asked us to resume with some other signal. + + if (GetResumeSignal() == LLDB_INVALID_SIGNAL_NUMBER) + { + Thread::StopInfo stop_info; + GetRawStopReason(&stop_info); + + StopReason reason = stop_info.GetStopReason(); + if (reason == eStopReasonSignal) + { + UnixSignals &signals = GetProcess().GetUnixSignals(); + int32_t signo = stop_info.GetSignal(); + if (!signals.GetShouldSuppress(signo)) + { + SetResumeSignal(signo); + } + } + } + + // Tell all the plans that we are about to resume in case they need to clear any state. + // We distinguish between the plan on the top of the stack and the lower + // plans in case a plan needs to do any special business before it runs. + + ThreadPlan *plan_ptr = GetCurrentPlan(); + plan_ptr->WillResume(resume_state, true); + + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL) + { + plan_ptr->WillResume (resume_state, false); + } + return true; +} + +void +Thread::DidResume () +{ + SetResumeSignal (LLDB_INVALID_SIGNAL_NUMBER); +} + +bool +Thread::ShouldStop (Event* event_ptr) +{ + ThreadPlan *current_plan = GetCurrentPlan(); + bool should_stop = true; + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + StreamString s; + DumpThreadPlans(&s); + log->PutCString (s.GetData()); + } + + if (current_plan->PlanExplainsStop()) + { + while (1) + { + should_stop = current_plan->ShouldStop(event_ptr); + if (current_plan->MischiefManaged()) + { + if (should_stop) + current_plan->WillStop(); + + // If a Master Plan wants to stop, and wants to stick on the stack, we let it. + // Otherwise, see if the plan's parent wants to stop. + + if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard()) + { + PopPlan(); + break; + } + else + { + + PopPlan(); + + current_plan = GetCurrentPlan(); + if (current_plan == NULL) + { + break; + } + } + + } + else + { + break; + } + } + } + else + { + // If the current plan doesn't explain the stop, then, find one that + // does and let it handle the situation. + ThreadPlan *plan_ptr = current_plan; + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL) + { + if (plan_ptr->PlanExplainsStop()) + { + should_stop = plan_ptr->ShouldStop (event_ptr); + break; + } + + } + } + + return should_stop; +} + +Vote +Thread::ShouldReportStop (Event* event_ptr) +{ + StateType thread_state = GetResumeState (); + if (thread_state == eStateSuspended + || thread_state == eStateInvalid) + return eVoteNoOpinion; + + if (m_completed_plan_stack.size() > 0) + { + // Don't use GetCompletedPlan here, since that suppresses private plans. + return m_completed_plan_stack.back()->ShouldReportStop (event_ptr); + } + else + return GetCurrentPlan()->ShouldReportStop (event_ptr); +} + +Vote +Thread::ShouldReportRun (Event* event_ptr) +{ + StateType thread_state = GetResumeState (); + if (thread_state == eStateSuspended + || thread_state == eStateInvalid) + return eVoteNoOpinion; + + if (m_completed_plan_stack.size() > 0) + { + // Don't use GetCompletedPlan here, since that suppresses private plans. + return m_completed_plan_stack.back()->ShouldReportRun (event_ptr); + } + else + return GetCurrentPlan()->ShouldReportRun (event_ptr); +} + +void +Thread::PushPlan (ThreadPlanSP &thread_plan_sp) +{ + if (thread_plan_sp) + { + if (thread_plan_sp->IsImmediate()) + m_immediate_plan_stack.push_back (thread_plan_sp); + else + m_plan_stack.push_back (thread_plan_sp); + + thread_plan_sp->DidPush(); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + StreamString s; + thread_plan_sp->GetDescription (&s, lldb::eDescriptionLevelFull); + log->Printf("Pushing plan: \"%s\" for thread: %d immediate: %s.", + s.GetData(), + thread_plan_sp->GetThread().GetID(), + thread_plan_sp->IsImmediate() ? "true" : "false"); + } + } +} + +void +Thread::PopPlan () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (!m_immediate_plan_stack.empty()) + { + ThreadPlanSP &plan = m_immediate_plan_stack.back(); + if (log) + { + log->Printf("Popping plan: \"%s\" for thread: %d immediate: true.", plan->GetName(), plan->GetThread().GetID()); + } + plan->WillPop(); + m_immediate_plan_stack.pop_back(); + } + else if (m_plan_stack.empty()) + return; + else + { + ThreadPlanSP &plan = m_plan_stack.back(); + if (log) + { + log->Printf("Popping plan: \"%s\" for thread: 0x%x immediate: false.", plan->GetName(), plan->GetThread().GetID()); + } + m_completed_plan_stack.push_back (plan); + plan->WillPop(); + m_plan_stack.pop_back(); + } +} + +void +Thread::DiscardPlan () +{ + if (m_plan_stack.size() > 1) + { + ThreadPlanSP &plan = m_plan_stack.back(); + m_discarded_plan_stack.push_back (plan); + plan->WillPop(); + m_plan_stack.pop_back(); + } +} + +ThreadPlan * +Thread::GetCurrentPlan () +{ + if (!m_immediate_plan_stack.empty()) + return m_immediate_plan_stack.back().get(); + else if (m_plan_stack.empty()) + return NULL; + else + return m_plan_stack.back().get(); +} + +ThreadPlanSP +Thread::GetCompletedPlan () +{ + ThreadPlanSP empty_plan_sp; + if (!m_completed_plan_stack.empty()) + { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) + { + ThreadPlanSP completed_plan_sp; + completed_plan_sp = m_completed_plan_stack[i]; + if (!completed_plan_sp->GetPrivate ()) + return completed_plan_sp; + } + } + return empty_plan_sp; +} + +bool +Thread::IsThreadPlanDone (ThreadPlan *plan) +{ + ThreadPlanSP empty_plan_sp; + if (!m_completed_plan_stack.empty()) + { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) + { + if (m_completed_plan_stack[i].get() == plan) + return true; + } + } + return false; +} + +bool +Thread::WasThreadPlanDiscarded (ThreadPlan *plan) +{ + ThreadPlanSP empty_plan_sp; + if (!m_discarded_plan_stack.empty()) + { + for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) + { + if (m_discarded_plan_stack[i].get() == plan) + return true; + } + } + return false; +} + +ThreadPlan * +Thread::GetPreviousPlan (ThreadPlan *current_plan) +{ + if (current_plan == NULL) + return NULL; + + int stack_size = m_completed_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + if (current_plan == m_completed_plan_stack[i].get()) + return m_completed_plan_stack[i-1].get(); + } + + if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) + { + if (m_immediate_plan_stack.size() > 0) + return m_immediate_plan_stack.back().get(); + else if (m_plan_stack.size() > 0) + return m_plan_stack.back().get(); + else + return NULL; + } + + stack_size = m_immediate_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + if (current_plan == m_immediate_plan_stack[i].get()) + return m_immediate_plan_stack[i-1].get(); + } + if (stack_size > 0 && m_immediate_plan_stack[0].get() == current_plan) + { + if (m_plan_stack.size() > 0) + return m_plan_stack.back().get(); + else + return NULL; + } + + stack_size = m_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + if (current_plan == m_plan_stack[i].get()) + return m_plan_stack[i-1].get(); + } + return NULL; +} + +void +Thread::QueueThreadPlan (ThreadPlanSP &thread_plan_sp, bool abort_other_plans) +{ + if (abort_other_plans) + DiscardThreadPlans(true); + + PushPlan (thread_plan_sp); +} + +void +Thread::DiscardThreadPlans(bool force) +{ + // FIXME: It is not always safe to just discard plans. Some, like the step over + // breakpoint trap can't be discarded in general (though you can if you plan to + // force a return from a function, for instance. + // For now I'm just not clearing immediate plans, but I need a way for plans to + // say they really need to be kept on, and then a way to override that. Humm... + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + log->Printf("Discarding thread plans for thread: 0x%x: force %d.", GetID(), force); + } + + if (force) + { + int stack_size = m_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + DiscardPlan(); + } + return; + } + + while (1) + { + + int master_plan_idx; + bool discard; + + // Find the first master plan, see if it wants discarding, and if yes discard up to it. + for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0; master_plan_idx--) + { + if (m_plan_stack[master_plan_idx]->IsMasterPlan()) + { + discard = m_plan_stack[master_plan_idx]->OkayToDiscard(); + break; + } + } + + if (discard) + { + // First pop all the dependent plans: + for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) + { + + // FIXME: Do we need a finalize here, or is the rule that "PrepareForStop" + // for the plan leaves it in a state that it is safe to pop the plan + // with no more notice? + DiscardPlan(); + } + + // Now discard the master plan itself. + // The bottom-most plan never gets discarded. "OkayToDiscard" for it means + // discard it's dependent plans, but not it... + if (master_plan_idx > 0) + { + DiscardPlan(); + } + } + else + { + // If the master plan doesn't want to get discarded, then we're done. + break; + } + + } + // FIXME: What should we do about the immediate plans? +} + +ThreadPlan * +Thread::QueueFundamentalPlan (bool abort_other_plans) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanBase(*this)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepSingleInstruction (bool step_over, bool abort_other_plans, bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepInstruction (*this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepRange (bool abort_other_plans, StepType type, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_other_threads) +{ + ThreadPlanSP thread_plan_sp; + if (type == eStepTypeInto) + thread_plan_sp.reset (new ThreadPlanStepInRange (*this, range, addr_context, stop_other_threads)); + else + thread_plan_sp.reset (new ThreadPlanStepOverRange (*this, range, addr_context, stop_other_threads)); + + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + + +ThreadPlan * +Thread::QueueThreadPlanForStepOverBreakpointPlan (bool abort_other_plans) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepOverBreakpoint (*this)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepOut (bool abort_other_plans, SymbolContext *addr_context, bool first_insn, + bool stop_other_threads, Vote stop_vote, Vote run_vote) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepOut (*this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepThrough (bool abort_other_plans, bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp(GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (*this, stop_other_threads)); + if (thread_plan_sp.get() == NULL) + { + thread_plan_sp.reset(new ThreadPlanStepThrough (*this, stop_other_threads)); + if (thread_plan_sp && !thread_plan_sp->ValidatePlan (NULL)) + return false; + } + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForContinue (bool abort_other_plans, bool stop_other_threads, Vote stop_vote, Vote run_vote, bool immediate) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanContinue (*this, stop_other_threads, stop_vote, run_vote, immediate)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + lldb::addr_t arg, + bool stop_other_threads, + bool discard_on_error) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, arg, stop_other_threads, discard_on_error)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + ValueList &args, + bool stop_other_threads, + bool discard_on_error) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, args, stop_other_threads, discard_on_error)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForRunToAddress (bool abort_other_plans, + Address &target_addr, + bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanRunToAddress (*this, target_addr, stop_other_threads)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepUntil (*this, address_list, num_addresses, stop_other_threads)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); + +} + +uint32_t +Thread::GetIndexID () const +{ + return m_index_id; +} + +void +Thread::DumpThreadPlans (lldb_private::Stream *s) const +{ + uint32_t stack_size = m_plan_stack.size(); + s->Printf ("Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + + stack_size = m_immediate_plan_stack.size(); + s->Printf ("Immediate Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_immediate_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + + stack_size = m_completed_plan_stack.size(); + s->Printf ("Completed Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_completed_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + + stack_size = m_discarded_plan_stack.size(); + s->Printf ("Discarded Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_discarded_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + +} + +Target * +Thread::CalculateTarget () +{ + return m_process.CalculateTarget(); +} + +Process * +Thread::CalculateProcess () +{ + return &m_process; +} + +Thread * +Thread::CalculateThread () +{ + return this; +} + +StackFrame * +Thread::CalculateStackFrame () +{ + return NULL; +} + +void +Thread::Calculate (ExecutionContext &exe_ctx) +{ + m_process.Calculate (exe_ctx); + exe_ctx.thread = this; + exe_ctx.frame = NULL; +} + +lldb::StackFrameSP +Thread::GetCurrentFrame () +{ + return GetStackFrameAtIndex (m_frames.GetCurrentFrameIndex()); +} + +uint32_t +Thread::SetCurrentFrame (lldb_private::StackFrame *frame) +{ + return m_frames.SetCurrentFrame(frame); +} + +void +Thread::SetCurrentFrameByIndex (uint32_t frame_idx) +{ + m_frames.SetCurrentFrameByIndex(frame_idx); +} + +void +Thread::DumpInfo +( + Stream &strm, + bool show_stop_reason, + bool show_name, + bool show_queue, + uint32_t frame_idx +) +{ + strm.Printf("thread #%u: tid = 0x%4.4x", GetIndexID(), GetID()); + + if (frame_idx != LLDB_INVALID_INDEX32) + { + StackFrameSP frame_sp(GetStackFrameAtIndex (frame_idx)); + if (frame_sp) + { + strm.PutCString(", "); + frame_sp->Dump (&strm, false); + } + } + + if (show_stop_reason) + { + Thread::StopInfo thread_stop_info; + if (GetStopInfo(&thread_stop_info)) + { + if (thread_stop_info.GetStopReason() != eStopReasonNone) + { + strm.PutCString(", stop reason = "); + thread_stop_info.Dump(&strm); + } + } + } + + if (show_name) + { + const char *name = GetName(); + if (name && name[0]) + strm.Printf(", name = %s", name); + } + + if (show_queue) + { + const char *queue = GetQueueName(); + if (queue && queue[0]) + strm.Printf(", queue = %s", queue); + } +} + +lldb::ThreadSP +Thread::GetSP () +{ + return m_process.GetThreadList().GetThreadSPForThreadPtr(this); +} diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp new file mode 100644 index 000000000000..6bc22712d107 --- /dev/null +++ b/lldb/source/Target/ThreadList.cpp @@ -0,0 +1,460 @@ +//===-- ThreadList.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 "lldb/Target/ThreadList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadList::ThreadList (Process *process) : + m_process (process), + m_stop_id (0), + m_threads(), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_current_tid (LLDB_INVALID_THREAD_ID) +{ +} + +ThreadList::ThreadList (const ThreadList &rhs) : + m_process (), + m_stop_id (), + m_threads (), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_current_tid () +{ + // Use the assignment operator since it uses the mutex + *this = rhs; +} + +const ThreadList& +ThreadList::operator = (const ThreadList& rhs) +{ + if (this != &rhs) + { + // Lock both mutexes to make sure neither side changes anyone on us + // while the assignement occurs + Mutex::Locker locker_this(m_threads_mutex); + Mutex::Locker locker_rhs(rhs.m_threads_mutex); + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads = rhs.m_threads; + m_current_tid = rhs.m_current_tid; + } + return *this; +} + + +ThreadList::~ThreadList() +{ +} + + +uint32_t +ThreadList::GetStopID () const +{ + return m_stop_id; +} + +void +ThreadList::SetStopID (uint32_t stop_id) +{ + m_stop_id = stop_id; +} + + +void +ThreadList::AddThread (ThreadSP &thread_sp) +{ + Mutex::Locker locker(m_threads_mutex); + m_threads.push_back(thread_sp); +} + +uint32_t +ThreadList::GetSize (bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + return m_threads.size(); +} + +ThreadSP +ThreadList::GetThreadAtIndex (uint32_t idx, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} + +ThreadSP +ThreadList::FindThreadByID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetID() == tid) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP +ThreadList::GetThreadSPForThreadPtr (Thread *thread_ptr) +{ + ThreadSP thread_sp; + if (thread_ptr) + { + Mutex::Locker locker(m_threads_mutex); + + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx].get() == thread_ptr) + { + thread_sp = m_threads[idx]; + break; + } + } + } + return thread_sp; +} + + + +ThreadSP +ThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetIndexID() == index_id) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +bool +ThreadList::ShouldStop (Event *event_ptr) +{ + Mutex::Locker locker(m_threads_mutex); + + // Running events should never stop, obviously... + + + bool should_stop = false; + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should stop. Don't ask + // suspended threads, however, it makes more sense for them to preserve their + // state across the times the process runs but they don't get a chance to. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if ((thread_sp->ThreadStoppedForAReason()) + && (thread_sp->GetResumeState () != eStateSuspended)) + { + should_stop |= thread_sp->ShouldStop(event_ptr); + } + } + if (should_stop) + { + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillStop (); + } + } + + return should_stop; +} + +Vote +ThreadList::ShouldReportStop (Event *event_ptr) +{ + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // For stopping, a YES vote wins over everything. A NO vote wins over NO opinion. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->ThreadStoppedForAReason() && (thread_sp->GetResumeState () != eStateSuspended)) + { + switch (thread_sp->ShouldReportStop (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + result = eVoteYes; + break; + case eVoteNo: + if (result == eVoteNoOpinion) + result = eVoteNo; + break; + } + } + } + return result; +} + +Vote +ThreadList::ShouldReportRun (Event *event_ptr) +{ + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // The rule is NO vote wins over everything, a YES vote wins over no opinion. + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState () != eStateSuspended) + + switch (thread_sp->ShouldReportRun (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + if (result == eVoteNoOpinion) + result = eVoteYes; + break; + case eVoteNo: + result = eVoteNo; + break; + } + } + return result; +} + +void +ThreadList::Clear() +{ + m_stop_id = 0; + m_threads.clear(); + m_current_tid = LLDB_INVALID_THREAD_ID; +} + +void +ThreadList::RefreshStateAfterStop () +{ + Mutex::Locker locker(m_threads_mutex); + + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->RefreshStateAfterStop (); +} + +void +ThreadList::DiscardThreadPlans () +{ + // You don't need to update the thread list here, because only threads + // that you currently know about have any thread plans. + Mutex::Locker locker(m_threads_mutex); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->DiscardThreadPlans (true); + +} + +bool +ThreadList::WillResume () +{ + // Run through the threads and perform their momentary actions. + // But we only do this for threads that are running, user suspended + // threads stay where they are. + bool success = true; + + Mutex::Locker locker(m_threads_mutex); + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // Give all the threads a last chance to set up their state before we + // negotiate who is actually going to get a chance to run... + + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->SetupForResume (); + + // Now go through the threads and see if any thread wants to run just itself. + // if so then pick one and run it. + ThreadList run_me_only_list (m_process); + + run_me_only_list.SetStopID(m_process->GetStopID()); + + ThreadSP immediate_thread_sp; + bool run_only_current_thread = false; + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetCurrentPlan()->IsImmediate()) + { + // We first do all the immediate plans, so if we find one, set + // immediate_thread_sp and break out, and we'll pick it up first thing + // when we're negotiating which threads get to run. + immediate_thread_sp = thread_sp; + break; + } + else if (thread_sp->GetResumeState() != eStateSuspended && + thread_sp->GetCurrentPlan()->StopOthers()) + { + // You can't say "stop others" and also want yourself to be suspended. + assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); + + if (thread_sp == GetCurrentThread()) + { + run_only_current_thread = true; + run_me_only_list.Clear(); + run_me_only_list.AddThread (thread_sp); + break; + } + + run_me_only_list.AddThread (thread_sp); + } + + } + + if (immediate_thread_sp) + { + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp.get() == immediate_thread_sp.get()) + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + else + thread_sp->WillResume (eStateSuspended); + } + } + else if (run_me_only_list.GetSize (false) == 0) + { + // Everybody runs as they wish: + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + } + } + else + { + ThreadSP thread_to_run; + + if (run_only_current_thread) + { + thread_to_run = GetCurrentThread(); + } + else if (run_me_only_list.GetSize (false) == 1) + { + thread_to_run = run_me_only_list.GetThreadAtIndex (0); + } + else + { + int random_thread = (int) + ((run_me_only_list.GetSize (false) * (double) rand ()) / (RAND_MAX + 1.0)); + thread_to_run = run_me_only_list.GetThreadAtIndex (random_thread); + } + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp == thread_to_run) + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + else + thread_sp->WillResume (eStateSuspended); + } + } + + return success; +} + +void +ThreadList::DidResume () +{ + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + // Don't clear out threads that aren't going to get a chance to run, rather + // leave their state for the next time around. + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended) + thread_sp->DidResume (); + } +} + +ThreadSP +ThreadList::GetCurrentThread () +{ + Mutex::Locker locker(m_threads_mutex); + return FindThreadByID(m_current_tid); +} + +bool +ThreadList::SetCurrentThreadByID (lldb::tid_t tid) +{ + Mutex::Locker locker(m_threads_mutex); + if (FindThreadByID(tid).get()) + m_current_tid = tid; + else + m_current_tid = LLDB_INVALID_THREAD_ID; + + return m_current_tid != LLDB_INVALID_THREAD_ID; +} + +bool +ThreadList::SetCurrentThreadByIndexID (uint32_t index_id) +{ + Mutex::Locker locker(m_threads_mutex); + ThreadSP thread_sp (FindThreadByIndexID(index_id)); + if (thread_sp.get()) + m_current_tid = thread_sp->GetID(); + else + m_current_tid = LLDB_INVALID_THREAD_ID; + + return m_current_tid != LLDB_INVALID_THREAD_ID; +} + diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp new file mode 100644 index 000000000000..c9005c1f345f --- /dev/null +++ b/lldb/source/Target/ThreadPlan.cpp @@ -0,0 +1,185 @@ +//===-- ThreadPlan.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/Target/ThreadPlan.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlan constructor +//---------------------------------------------------------------------- +ThreadPlan::ThreadPlan(const char *name, Thread &thread, Vote stop_vote, Vote run_vote) : + m_name (name), + m_thread (thread), + m_plan_complete(false), + m_plan_complete_mutex (Mutex::eMutexTypeRecursive), + m_plan_private (false), + m_stop_vote (stop_vote), + m_run_vote (run_vote), + m_okay_to_discard (false) +{ + SetID (GetNextID()); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlan::~ThreadPlan() +{ +} + +const char * +ThreadPlan::GetName () const +{ + return m_name.c_str(); +} + +Thread & +ThreadPlan::GetThread() +{ + return m_thread; +} + + +const Thread & +ThreadPlan::GetThread() const +{ + return m_thread; +} + +bool +ThreadPlan::IsPlanComplete () +{ + Mutex::Locker (m_plan_complete_mutex); + return m_plan_complete; +} + +void +ThreadPlan::SetPlanComplete () +{ + Mutex::Locker (m_plan_complete_mutex); + m_plan_complete = true; +} + +bool +ThreadPlan::MischiefManaged () +{ + Mutex::Locker (m_plan_complete_mutex); + m_plan_complete = true; + return true; +} + +Vote +ThreadPlan::ShouldReportStop (Event *event_ptr) +{ + if (m_stop_vote == eVoteNoOpinion) + { + ThreadPlan *prev_plan = GetPreviousPlan (); + if (prev_plan) + return prev_plan->ShouldReportStop (event_ptr); + } + return m_stop_vote; +} + +Vote +ThreadPlan::ShouldReportRun (Event *event_ptr) +{ + if (m_run_vote == eVoteNoOpinion) + { + ThreadPlan *prev_plan = GetPreviousPlan (); + if (prev_plan) + return prev_plan->ShouldReportRun (event_ptr); + } + return m_run_vote; +} + +bool +ThreadPlan::StopOthers () +{ + ThreadPlan *prev_plan; + prev_plan = GetPreviousPlan (); + if (prev_plan == NULL) + return false; + else + return prev_plan->StopOthers(); +} + +bool +ThreadPlan::WillResume (StateType resume_state, bool current_plan) +{ + if (current_plan) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (log) + log->Printf("About to resume the \"%s\" plan - state: %s - stop others: %d.", + m_name.c_str(), StateAsCString(resume_state), StopOthers()); + } + return true; +} + +lldb::user_id_t +ThreadPlan::GetNextID() +{ + static uint32_t g_nextPlanID = 0; + return ++g_nextPlanID; +} + +void +ThreadPlan::DidPush() +{ +} + +void +ThreadPlan::WillPop() +{ +} + +void +ThreadPlan::PushPlan (ThreadPlanSP &thread_plan_sp) +{ + m_thread.PushPlan (thread_plan_sp); +} + +ThreadPlan * +ThreadPlan::GetPreviousPlan () +{ + return m_thread.GetPreviousPlan (this); +} + +void +ThreadPlan::SetPrivate (bool input) +{ + m_plan_private = input; +} + +bool +ThreadPlan::GetPrivate (void) +{ + return m_plan_private; +} + +bool +ThreadPlan::OkayToDiscard() +{ + if (!IsMasterPlan()) + return true; + else + return m_okay_to_discard; +} + diff --git a/lldb/source/Target/ThreadPlanBase.cpp b/lldb/source/Target/ThreadPlanBase.cpp new file mode 100644 index 000000000000..283f3bef4c95 --- /dev/null +++ b/lldb/source/Target/ThreadPlanBase.cpp @@ -0,0 +1,202 @@ +//===-- ThreadPlanBase.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/Target/ThreadPlanBase.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +// +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/ThreadPlanContinue.h" +#include "lldb/Target/ThreadPlanStepOverBreakpoint.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanBase: This one always stops, and never has anything particular +// to do. +// FIXME: The "signal handling" policies should probably go here. +//---------------------------------------------------------------------- + +ThreadPlanBase::ThreadPlanBase (Thread &thread) : + ThreadPlan("base plan", thread, eVoteYes, eVoteNoOpinion) +{ + +} + +ThreadPlanBase::~ThreadPlanBase () +{ + +} + +void +ThreadPlanBase::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + s->Printf ("Base thread plan."); +} + +bool +ThreadPlanBase::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanBase::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanBase::ShouldStop (Event *event_ptr) +{ + m_stop_vote = eVoteYes; + m_run_vote = eVoteYes; + + Thread::StopInfo stop_info; + if (m_thread.GetStopInfo(&stop_info)) + { + StopReason reason = stop_info.GetStopReason(); + switch (reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + { + m_run_vote = eVoteNo; + m_stop_vote = eVoteNo; + return false; + } + case eStopReasonBreakpoint: + { + // The base plan checks for breakpoint hits. + + BreakpointSiteSP bp_site_sp; + //RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + //lldb::addr_t pc = reg_ctx->GetPC(); + bp_site_sp = m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info.GetBreakpointSiteID()); + + if (bp_site_sp && bp_site_sp->IsEnabled()) + { + // We want to step over the breakpoint and then continue. So push these two plans. + + StoppointCallbackContext hit_context(event_ptr, &m_thread.GetProcess(), &m_thread, m_thread.GetStackFrameAtIndex(0).get()); + bool should_stop = m_thread.GetProcess().GetBreakpointSiteList().ShouldStop(&hit_context, bp_site_sp->GetID()); + + if (!should_stop) + { + // If we aren't going to stop at this breakpoint, and it is internal, don't report this stop or the subsequent + // running event. Otherwise we will post the stopped & running, but the stopped event will get marked + // with "restarted" so the UI will know to wait and expect the consequent "running". + uint32_t i; + bool is_wholly_internal = true; + + for (i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) + { + if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) + { + is_wholly_internal = false; + break; + } + } + if (is_wholly_internal) + { + m_stop_vote = eVoteNo; + m_run_vote = eVoteNo; + } + else + { + m_stop_vote = eVoteYes; + m_run_vote = eVoteYes; + } + + } + else + { + // If we are going to stop for a breakpoint, then unship the other plans + // at this point. Don't force the discard, however, so Master plans can stay + // in place if they want to. + m_thread.DiscardThreadPlans(false); + } + + return should_stop; + } + } + case eStopReasonException: + // If we crashed, discard thread plans and stop. Don't force the discard, however, + // since on rerun the target may clean up this exception and continue normally from there. + m_thread.DiscardThreadPlans(false); + return true; + case eStopReasonSignal: + { + // Check the signal handling, and if we are stopping for the signal, + // discard the plans and stop. + UnixSignals &signals = m_thread.GetProcess().GetUnixSignals(); + uint32_t signo = stop_info.GetSignal(); + if (signals.GetShouldStop(signo)) + { + m_thread.DiscardThreadPlans(false); + return true; + } + else + { + // We're not going to stop, but while we are here, let's figure out + // whether to report this. + if (signals.GetShouldNotify(signo)) + m_stop_vote = eVoteYes; + else + m_stop_vote = eVoteNo; + + return false; + } + } + default: + return true; + } + + } + + // If there's no explicit reason to stop, then we will continue. + return false; +} + +bool +ThreadPlanBase::StopOthers () +{ + return false; +} + +StateType +ThreadPlanBase::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanBase::WillStop () +{ + return true; +} + +// The base plan is never done. +bool +ThreadPlanBase::MischiefManaged () +{ + // The base plan is never done. + return false; +} + diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp new file mode 100644 index 000000000000..4b3533a3446b --- /dev/null +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -0,0 +1,250 @@ +//===-- ThreadPlanCallFunction.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/Target/ThreadPlanCallFunction.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanCallFunction: Plan to call a single function +//---------------------------------------------------------------------- + +ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, + Address &function, + lldb::addr_t arg, + bool stop_other_threads, + bool discard_on_error) : + ThreadPlan ("Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), + m_process(thread.GetProcess()), + m_arg_addr (arg), + m_args (NULL), + m_thread(thread), + m_stop_other_threads(stop_other_threads) +{ + + SetOkayToDiscard (discard_on_error); + + Process& process = thread.GetProcess(); + Target& target = process.GetTarget(); + const ABI *abi = process.GetABI(); + + if (!abi) + return; + + lldb::addr_t spBelowRedZone = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize(); + + SymbolContextList contexts; + SymbolContext context; + ModuleSP executableModuleSP (target.GetExecutableModule()); + + if (!executableModuleSP || + !executableModuleSP->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts)) + return; + + contexts.GetContextAtIndex(0, context); + + m_start_addr = context.symbol->GetValue(); + lldb::addr_t StartLoadAddr = m_start_addr.GetLoadAddress(&process); + + if (!thread.SaveFrameZeroState(m_register_backup)) + return; + + m_function_addr = function; + lldb::addr_t FunctionLoadAddr = m_function_addr.GetLoadAddress(&process); + + if (!abi->PrepareTrivialCall(thread, + spBelowRedZone, + FunctionLoadAddr, + StartLoadAddr, + m_arg_addr)) + return; + + m_valid = true; +} + +ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, + Address &function, + ValueList &args, + bool stop_other_threads, + bool discard_on_error) : + ThreadPlan ("Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), + m_process(thread.GetProcess()), + m_arg_addr (0), + m_args (&args), + m_thread(thread), + m_stop_other_threads(stop_other_threads) +{ + + SetOkayToDiscard (discard_on_error); + + Process& process = thread.GetProcess(); + Target& target = process.GetTarget(); + const ABI *abi = process.GetABI(); + + if(!abi) + return; + + lldb::addr_t spBelowRedZone = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize(); + + SymbolContextList contexts; + SymbolContext context; + ModuleSP executableModuleSP (target.GetExecutableModule()); + + if (!executableModuleSP || + !executableModuleSP->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts)) + return; + + contexts.GetContextAtIndex(0, context); + + m_start_addr = context.symbol->GetValue(); + lldb::addr_t StartLoadAddr = m_start_addr.GetLoadAddress(&process); + + if(!thread.SaveFrameZeroState(m_register_backup)) + return; + + m_function_addr = function; + lldb::addr_t FunctionLoadAddr = m_function_addr.GetLoadAddress(&process); + + if (!abi->PrepareNormalCall(thread, + spBelowRedZone, + FunctionLoadAddr, + StartLoadAddr, + *m_args)) + return; + + m_valid = true; +} + +ThreadPlanCallFunction::~ThreadPlanCallFunction () +{ +} + +void +ThreadPlanCallFunction::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf("Function call thread plan"); + } + else + { + if (m_args) + s->Printf("Thread plan to call 0x%llx with parsed arguments", m_function_addr.GetLoadAddress(&m_process), m_arg_addr); + else + s->Printf("Thread plan to call 0x%llx void * argument at: 0x%llx", m_function_addr.GetLoadAddress(&m_process), m_arg_addr); + } +} + +bool +ThreadPlanCallFunction::ValidatePlan (Stream *error) +{ + if (!m_valid) + return false; + + return true; +} + +bool +ThreadPlanCallFunction::PlanExplainsStop () +{ + if (!m_subplan_sp) + return false; + else + return m_subplan_sp->PlanExplainsStop(); +} + +bool +ThreadPlanCallFunction::ShouldStop (Event *event_ptr) +{ + if (PlanExplainsStop()) + { + m_thread.RestoreSaveFrameZero(m_register_backup); + m_thread.ClearStackFrames(); + SetPlanComplete(); + return true; + } + else + { + return false; + } +} + +bool +ThreadPlanCallFunction::StopOthers () +{ + return m_stop_other_threads; +} + +void +ThreadPlanCallFunction::SetStopOthers (bool new_value) +{ + if (m_subplan_sp) + { + ThreadPlanRunToAddress *address_plan = static_cast(m_subplan_sp.get()); + address_plan->SetStopOthers(new_value); + } + m_stop_other_threads = new_value; +} + +StateType +ThreadPlanCallFunction::RunState () +{ + return eStateRunning; +} + +void +ThreadPlanCallFunction::DidPush () +{ + m_subplan_sp.reset(new ThreadPlanRunToAddress(m_thread, m_start_addr, m_stop_other_threads)); + + m_thread.QueueThreadPlan(m_subplan_sp, false); + +} + +bool +ThreadPlanCallFunction::WillStop () +{ + return true; +} + +bool +ThreadPlanCallFunction::MischiefManaged () +{ + if (IsPlanComplete()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (log) + log->Printf("Completed call function plan."); + + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} diff --git a/lldb/source/Target/ThreadPlanContinue.cpp b/lldb/source/Target/ThreadPlanContinue.cpp new file mode 100644 index 000000000000..63d8a3323d03 --- /dev/null +++ b/lldb/source/Target/ThreadPlanContinue.cpp @@ -0,0 +1,120 @@ +//===-- ThreadPlanContinue.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/Target/ThreadPlanContinue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanContinue: Continue plan +//---------------------------------------------------------------------- + +ThreadPlanContinue::ThreadPlanContinue (Thread &thread, bool stop_others, Vote stop_vote, Vote run_vote, bool immediate) : + ThreadPlan ("Continue after previous plan", thread, stop_vote, run_vote), + m_stop_others (stop_others), + m_did_run (false), + m_immediate (immediate) +{ +} + +ThreadPlanContinue::~ThreadPlanContinue () +{ +} + +void +ThreadPlanContinue::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("continue"); + else + { + s->Printf ("Continue from the previous plan"); + } +} + +bool +ThreadPlanContinue::ValidatePlan (Stream *error) +{ + // Since we read the instruction we're stepping over from the thread, + // this plan will always work. + return true; +} + +bool +ThreadPlanContinue::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanContinue::ShouldStop (Event *event_ptr) +{ + return false; +} + +bool +ThreadPlanContinue::IsImmediate () const +{ + return m_immediate; + return false; +} + +bool +ThreadPlanContinue::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanContinue::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanContinue::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (current_plan) + { + m_did_run = true; + } + return true; +} + +bool +ThreadPlanContinue::WillStop () +{ + return true; +} + +bool +ThreadPlanContinue::MischiefManaged () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (m_did_run) + { + if (log) + log->Printf("Completed continue plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + return false; +} diff --git a/lldb/source/Target/ThreadPlanRunToAddress.cpp b/lldb/source/Target/ThreadPlanRunToAddress.cpp new file mode 100644 index 000000000000..0544ea5bcbe7 --- /dev/null +++ b/lldb/source/Target/ThreadPlanRunToAddress.cpp @@ -0,0 +1,176 @@ +//===-- ThreadPlanRunToAddress.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/Target/ThreadPlanRunToAddress.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanRunToAddress: Continue plan +//---------------------------------------------------------------------- + +ThreadPlanRunToAddress::ThreadPlanRunToAddress +( + Thread &thread, + Address &address, + bool stop_others +) : + ThreadPlan ("Run to breakpoint plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_stop_others (stop_others), + m_address (LLDB_INVALID_ADDRESS), + m_break_id (LLDB_INVALID_BREAK_ID) +{ + m_address = address.GetLoadAddress(&m_thread.GetProcess()); + SetInitialBreakpoint(); +} + +ThreadPlanRunToAddress::ThreadPlanRunToAddress +( + Thread &thread, + lldb::addr_t address, + bool stop_others +) : + ThreadPlan ("Run to breakpoint plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_stop_others (stop_others), + m_address (address), + m_break_id (LLDB_INVALID_BREAK_ID) +{ + SetInitialBreakpoint(); +} + +void +ThreadPlanRunToAddress::SetInitialBreakpoint () +{ + Breakpoint *breakpoint; + breakpoint = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_address, true).get(); + if (breakpoint != NULL) + { + m_break_id = breakpoint->GetID(); + breakpoint->SetThreadID(m_thread.GetID()); + } +} + +ThreadPlanRunToAddress::~ThreadPlanRunToAddress () +{ + if (m_break_id != LLDB_INVALID_BREAK_ID) + { + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_break_id); + } +} + +void +ThreadPlanRunToAddress::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf ("run to address: "); + s->Address (m_address, sizeof (addr_t)); + } + else + { + s->Printf ("Run to address: "); + s->Address(m_address, sizeof (addr_t)); + s->Printf (" using breakpoint: %d - ", m_break_id); + Breakpoint *breakpoint = m_thread.GetProcess().GetTarget().GetBreakpointByID (m_break_id).get(); + if (breakpoint) + breakpoint->Dump (s); + else + s->Printf ("but the breakpoint has been deleted."); + } +} + +bool +ThreadPlanRunToAddress::ValidatePlan (Stream *error) +{ + // If we couldn't set the breakpoint for some reason, then this won't + // work. + if(m_break_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +bool +ThreadPlanRunToAddress::PlanExplainsStop () +{ + return AtOurAddress(); +} + +bool +ThreadPlanRunToAddress::ShouldStop (Event *event_ptr) +{ + return false; +} + +bool +ThreadPlanRunToAddress::StopOthers () +{ + return m_stop_others; +} + +void +ThreadPlanRunToAddress::SetStopOthers (bool new_value) +{ + m_stop_others = new_value; +} + +StateType +ThreadPlanRunToAddress::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanRunToAddress::WillStop () +{ + return true; +} + +bool +ThreadPlanRunToAddress::MischiefManaged () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (AtOurAddress()) + { + // Remove the breakpoint + if (m_break_id != LLDB_INVALID_BREAK_ID) + { + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_break_id); + m_break_id = LLDB_INVALID_BREAK_ID; + } + + if (log) + log->Printf("Completed run to address plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + return false; +} + +bool +ThreadPlanRunToAddress::AtOurAddress () +{ + lldb::addr_t current_address = m_thread.GetRegisterContext()->GetPC(); + return m_address == current_address; +} diff --git a/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/lldb/source/Target/ThreadPlanShouldStopHere.cpp new file mode 100644 index 000000000000..493ab63bc333 --- /dev/null +++ b/lldb/source/Target/ThreadPlanShouldStopHere.cpp @@ -0,0 +1,53 @@ +//===-- ThreadPlanShouldStopHere.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/Target/Thread.h" +#include "lldb/Target/ThreadPlanShouldStopHere.h" + +using namespace lldb; +using namespace lldb_private; + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// ThreadPlanShouldStopHere constructor +//---------------------------------------------------------------------- +ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner, ThreadPlanShouldStopHereCallback callback, void *baton) : + m_callback (callback), + m_baton (baton), + m_owner (owner), + m_flags (ThreadPlanShouldStopHere::eNone) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() +{ +} + +void +ThreadPlanShouldStopHere::SetShouldStopHereCallback (ThreadPlanShouldStopHereCallback callback, void *baton) +{ + m_callback = callback; + m_baton = baton; +} + +ThreadPlan * +ThreadPlanShouldStopHere::InvokeShouldStopHereCallback () +{ + if (m_callback) + return m_callback (m_owner, m_flags, m_baton); + else + return NULL; +} \ No newline at end of file diff --git a/lldb/source/Target/ThreadPlanStepInRange.cpp b/lldb/source/Target/ThreadPlanStepInRange.cpp new file mode 100644 index 000000000000..f62fdb87c7fe --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepInRange.cpp @@ -0,0 +1,154 @@ +//===-- ThreadPlanStepInRange.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/Target/ThreadPlanStepInRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" + +using namespace lldb; +using namespace lldb_private; + +uint32_t ThreadPlanStepInRange::s_default_flag_values = ThreadPlanShouldStopHere::eAvoidNoDebug; + +//---------------------------------------------------------------------- +// ThreadPlanStepInRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepInRange::ThreadPlanStepInRange +( + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others +) : + ThreadPlanStepRange ("Step Range stepping in", thread, range, addr_context, stop_others), + ThreadPlanShouldStopHere (this, ThreadPlanStepInRange::DefaultShouldStopHereCallback, NULL) +{ + SetFlagsToDefault (); +} + +ThreadPlanStepInRange::~ThreadPlanStepInRange () +{ +} + +void +ThreadPlanStepInRange::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("step in"); + else + { + s->Printf ("Stepping through range (stepping into functions): "); + m_address_range.Dump (s, &m_thread.GetProcess(), Address::DumpStyleLoadAddress); + } +} + +bool +ThreadPlanStepInRange::ShouldStop (Event *event_ptr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + m_no_more_plans = false; + + if (log) + { + StreamString s; + s.Address (m_thread.GetRegisterContext()->GetPC(), m_thread.GetProcess().GetAddressByteSize()); + log->Printf("ThreadPlanStepInRange reached %s.", s.GetData()); + } + + // If we're still in the range, keep going. + if (InRange()) + return false; + + // If we're in an older frame then we should stop. + if (FrameIsOlder()) + return true; + + // See if we are in a place we should step through (i.e. a trampoline of some sort): + // One tricky bit here is that some stubs don't push a frame, so we have to check + // both the case of a frame that is younger, or the same as this frame. + // However, if the frame is the same, and we are still in the symbol we started + // in, the we don't need to do this. This first check isn't strictly necessary, + // but it is more efficient. + + if (!FrameIsYounger() && InSymbol()) + { + SetPlanComplete(); + return true; + } + + ThreadPlan* new_plan = NULL; + + bool stop_others; + if (m_stop_others == lldb::eOnlyThisThread) + stop_others = true; + else + stop_others = false; + + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + // If not, give the "should_stop" callback a chance to push a plan to get us out of here. + // But only do that if we actually have stepped in. + if (!new_plan && FrameIsYounger()) + new_plan = InvokeShouldStopHereCallback(); + + if (new_plan == NULL) + { + m_no_more_plans = true; + SetPlanComplete(); + return true; + } + else + { + m_no_more_plans = false; + return false; + } +} + +void +ThreadPlanStepInRange::SetFlagsToDefault () +{ + GetFlags().Set(ThreadPlanStepInRange::s_default_flag_values); +} + +void +ThreadPlanStepInRange::SetDefaultFlagValue (uint32_t new_value) +{ + // TODO: Should we test this for sanity? + ThreadPlanStepInRange::s_default_flag_values = new_value; +} + +ThreadPlan * +ThreadPlanStepInRange::DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton) +{ + if (flags.IsSet(eAvoidNoDebug)) + { + StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); + + if (!frame->HasDebugInformation()) + { + // FIXME: Make sure the ThreadPlanForStepOut does the right thing with inlined functions. + return current_plan->GetThread().QueueThreadPlanForStepOut (false, NULL, true, current_plan->StopOthers(), eVoteNo, eVoteNoOpinion); + } + } + + return NULL; +} diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp new file mode 100644 index 000000000000..41c4373105b4 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp @@ -0,0 +1,191 @@ +//===-- ThreadPlanStepInstruction.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/Target/ThreadPlanStepInstruction.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepInstruction: Step over the current instruction +//---------------------------------------------------------------------- + +ThreadPlanStepInstruction::ThreadPlanStepInstruction +( + Thread &thread, + bool step_over, + bool stop_other_threads, + Vote stop_vote, + Vote run_vote +) : + ThreadPlan ("Step over single instruction", thread, stop_vote, run_vote), + m_instruction_addr (0), + m_step_over (step_over), + m_stack_depth(0), + m_stop_other_threads (stop_other_threads) +{ + m_instruction_addr = m_thread.GetRegisterContext()->GetPC(0); + m_stack_depth = m_thread.GetStackFrameCount(); +} + +ThreadPlanStepInstruction::~ThreadPlanStepInstruction () +{ +} + +void +ThreadPlanStepInstruction::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + if (m_step_over) + s->Printf ("instruction step over"); + else + s->Printf ("instruction step into"); + } + else + { + s->Printf ("Stepping one instruction past "); + s->Address(m_instruction_addr, sizeof (addr_t)); + if (m_step_over) + s->Printf(" stepping over calls"); + else + s->Printf(" stepping into calls"); + } +} + +bool +ThreadPlanStepInstruction::ValidatePlan (Stream *error) +{ + // Since we read the instruction we're stepping over from the thread, + // this plan will always work. + return true; +} + +bool +ThreadPlanStepInstruction::PlanExplainsStop () +{ + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + if (reason == eStopReasonTrace || reason ==eStopReasonNone) + return true; + else + return false; + } + return false; +} + +bool +ThreadPlanStepInstruction::ShouldStop (Event *event_ptr) +{ + if (m_step_over) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (m_thread.GetStackFrameCount() <= m_stack_depth) + { + if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) + { + SetPlanComplete(); + return true; + } + else + return false; + } + else + { + // We've stepped in, step back out again: + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + if (return_frame) + { + if (log) + { + StreamString s; + s.PutCString ("Stepped in to: "); + addr_t stop_addr = m_thread.GetStackFrameAtIndex(0)->GetPC().GetLoadAddress(&m_thread.GetProcess()); + s.Address (stop_addr, m_thread.GetProcess().GetAddressByteSize()); + s.PutCString (" stepping out to: "); + addr_t return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + s.Address (return_addr, m_thread.GetProcess().GetAddressByteSize()); + log->Printf("%s.", s.GetData()); + } + m_thread.QueueThreadPlanForStepOut(false, NULL, true, m_stop_other_threads, eVoteNo, eVoteNoOpinion); + return false; + } + else + { + if (log) + log->Printf("Could not find previous frame, stopping."); + SetPlanComplete(); + return true; + } + + } + + } + else + { + if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) + { + SetPlanComplete(); + return true; + } + else + return false; + } +} + +bool +ThreadPlanStepInstruction::StopOthers () +{ + return m_stop_other_threads; +} + +StateType +ThreadPlanStepInstruction::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepInstruction::WillStop () +{ + return true; +} + +bool +ThreadPlanStepInstruction::MischiefManaged () +{ + if (IsPlanComplete()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed single instruction step plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} + diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp new file mode 100644 index 000000000000..e05a8a440a1d --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOut.cpp @@ -0,0 +1,228 @@ +//===-- ThreadPlanStepOut.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/Target/ThreadPlanStepOut.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepOut: Step out of the current frame +//---------------------------------------------------------------------- + +ThreadPlanStepOut::ThreadPlanStepOut +( + Thread &thread, + SymbolContext *context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote +) : + ThreadPlan ("Step out", thread, stop_vote, run_vote), + m_step_from_context (context), + m_step_from_insn (LLDB_INVALID_ADDRESS), + m_return_addr (LLDB_INVALID_ADDRESS), + m_first_insn (first_insn), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_stop_others (stop_others) +{ + m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); + + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + if (return_frame) + { + m_return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get(); + if (return_bp != NULL) + { + return_bp->SetThreadID(m_thread.GetID()); + m_return_bp_id = return_bp->GetID(); + } + else + { + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + } + + m_stack_depth = m_thread.GetStackFrameCount(); +} + +ThreadPlanStepOut::~ThreadPlanStepOut () +{ + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) + m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id); +} + +void +ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("step out"); + else + { + s->Printf ("Stepping out from address 0x%llx to return address 0x%llx using breakpoint site %d", + (uint64_t)m_step_from_insn, + (uint64_t)m_return_addr, + m_return_bp_id); + } +} + +bool +ThreadPlanStepOut::ValidatePlan (Stream *error) +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +bool +ThreadPlanStepOut::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + { + // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened... + BreakpointSiteSP this_site = m_thread.GetProcess().GetBreakpointSiteList().FindByID (info.GetBreakpointSiteID()); + if (!this_site) + return false; + + if (this_site->IsBreakpointAtThisSite (m_return_bp_id)) + { + // If there was only one owner, then we're done. But if we also hit some + // user breakpoint on our way out, we should mark ourselves as done, but + // also not claim to explain the stop, since it is more important to report + // the user breakpoint than the step out completion. + + if (this_site->GetNumberOfOwners() == 1) + return true; + else + { + SetPlanComplete(); + return false; + } + } + else + return false; + } + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + return false; + default: + return true; + } + } + return true; +} + +bool +ThreadPlanStepOut::ShouldStop (Event *event_ptr) +{ + if (IsPlanComplete() + || m_thread.GetRegisterContext()->GetPC() == m_return_addr + || m_stack_depth > m_thread.GetStackFrameCount()) + { + SetPlanComplete(); + return true; + } + else + return false; +} + +bool +ThreadPlanStepOut::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepOut::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepOut::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + + if (current_plan) + { + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (true); + } + return true; +} + +bool +ThreadPlanStepOut::WillStop () +{ + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (false); + return true; +} + +bool +ThreadPlanStepOut::MischiefManaged () +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + { + // If I couldn't set this breakpoint, then I'm just going to jettison myself. + return true; + } + else if (IsPlanComplete()) + { + // Did I reach my breakpoint? If so I'm done. + // + // I also check the stack depth, since if we've blown past the breakpoint for some + // reason and we're now stopping for some other reason altogether, then we're done + // with this step out operation. + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step out plan."); + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} + diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp new file mode 100644 index 000000000000..2b8b06d59601 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -0,0 +1,130 @@ +//===-- ThreadPlanStepOverBreakpoint.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/Target/ThreadPlanStepOverBreakpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at the pc. +//---------------------------------------------------------------------- + +ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint (Thread &thread) : + ThreadPlan ("Step over breakpoint trap", + thread, + eVoteNo, + eVoteNoOpinion), // We need to report the run since this happens + // first in the thread plan stack when stepping + // over a breakpoint + m_breakpoint_addr (LLDB_INVALID_ADDRESS) +{ + m_breakpoint_addr = m_thread.GetRegisterContext()->GetPC(); + m_breakpoint_site_id = m_thread.GetProcess().GetBreakpointSiteList().FindIDByAddress (m_breakpoint_addr); + +} + +ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint () +{ +} + +void +ThreadPlanStepOverBreakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + s->Printf("Single stepping past breakpoint site %d at 0x%llx", m_breakpoint_site_id, (uint64_t)m_breakpoint_addr); +} + +bool +ThreadPlanStepOverBreakpoint::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepOverBreakpoint::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanStepOverBreakpoint::ShouldStop (Event *event_ptr) +{ + return false; +} + +bool +ThreadPlanStepOverBreakpoint::StopOthers () +{ + return true; +} + +StateType +ThreadPlanStepOverBreakpoint::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepOverBreakpoint::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + + if (current_plan) + { + BreakpointSiteSP bp_site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByAddress (m_breakpoint_addr)); + if (bp_site_sp && bp_site_sp->IsEnabled()) + m_thread.GetProcess().DisableBreakpoint (bp_site_sp.get()); + } + return true; +} + +bool +ThreadPlanStepOverBreakpoint::WillStop () +{ + BreakpointSiteSP bp_site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByAddress (m_breakpoint_addr)); + if (bp_site_sp) + m_thread.GetProcess().EnableBreakpoint (bp_site_sp.get()); + return true; +} + +bool +ThreadPlanStepOverBreakpoint::MischiefManaged () +{ + lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(); + + if (pc_addr == m_breakpoint_addr) + { + // If we are still at the PC of our breakpoint, then for some reason we didn't + // get a chance to run. + return false; + } + else + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step over breakpoint plan."); + // Otherwise, re-enable the breakpoint we were stepping over, and we're done. + BreakpointSiteSP bp_site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByAddress (m_breakpoint_addr)); + if (bp_site_sp) + m_thread.GetProcess().EnableBreakpoint (bp_site_sp.get()); + ThreadPlan::MischiefManaged (); + return true; + } +} + diff --git a/lldb/source/Target/ThreadPlanStepOverRange.cpp b/lldb/source/Target/ThreadPlanStepOverRange.cpp new file mode 100644 index 000000000000..ea56412a0107 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -0,0 +1,119 @@ +//===-- ThreadPlanStepOverRange.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/Target/ThreadPlanStepOverRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" + +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// ThreadPlanStepOverRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepOverRange::ThreadPlanStepOverRange +( + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others, + bool okay_to_discard +) : + ThreadPlanStepRange ("Step range stepping over", thread, range, addr_context, stop_others) +{ + SetOkayToDiscard (okay_to_discard); +} + +ThreadPlanStepOverRange::~ThreadPlanStepOverRange () +{ +} + +void +ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("step over"); + else + { + s->Printf ("stepping through range (stepping over functions): "); + m_address_range.Dump (s, &m_thread.GetProcess(), Address::DumpStyleLoadAddress); + } +} + +bool +ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (log) + { + StreamString s; + s.Address (m_thread.GetRegisterContext()->GetPC(), m_thread.GetProcess().GetAddressByteSize()); + log->Printf("ThreadPlanStepOverRange reached %s.", s.GetData()); + } + + // If we're still in the range, keep going. + if (InRange()) + return false; + + // If we're out of the range but in the same frame or in our caller's frame + // then we should stop. + // When stepping out we only step if we are forcing running one thread. + bool stop_others; + if (m_stop_others == lldb::eOnlyThisThread) + stop_others = true; + else + stop_others = false; + + ThreadPlan* new_plan = NULL; + + if (FrameIsOlder()) + return true; + else if (FrameIsYounger()) + { + new_plan = m_thread.QueueThreadPlanForStepOut (false, NULL, true, stop_others, lldb::eVoteNo, lldb::eVoteNoOpinion); + } + else if (!InSymbol()) + { + // This one is a little tricky. Sometimes we may be in a stub or something similar, + // in which case we need to get out of there. But if we are in a stub then it's + // likely going to be hard to get out from here. It is probably easiest to step into the + // stub, and then it will be straight-forward to step out. + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + } + + if (new_plan == NULL) + m_no_more_plans = true; + else + m_no_more_plans = false; + + if (new_plan == NULL) + { + // For efficiencies sake, we know we're done here so we don't have to do this + // calculation again in MischiefManaged. + SetPlanComplete(); + return true; + } + else + return false; +} diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp new file mode 100644 index 000000000000..edf80a57d520 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -0,0 +1,263 @@ +//===-- ThreadPlanStepRange.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/Target/ThreadPlanStepRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// ThreadPlanStepRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepRange::ThreadPlanStepRange (const char *name, Thread &thread, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_others) : + ThreadPlan (name, thread, eVoteNoOpinion, eVoteNoOpinion), + m_address_range (range), + m_addr_context (addr_context), + m_stop_others (stop_others), + m_stack_depth (0), + m_no_more_plans (false), + m_stack_id (), + m_first_run_event (true) +{ + m_stack_depth = m_thread.GetStackFrameCount(); + m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); +} + +ThreadPlanStepRange::~ThreadPlanStepRange () +{ +} + +bool +ThreadPlanStepRange::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepRange::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + return false; + default: + return true; + } + } + return true; +} + +Vote +ThreadPlanStepRange::ShouldReportStop (Event *event_ptr) +{ + if (IsPlanComplete()) + return eVoteYes; + else + return eVoteNo; +} + +bool +ThreadPlanStepRange::InRange () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + bool ret_value = false; + + lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC(); + + ret_value = m_address_range.ContainsLoadAddress(pc_load_addr, &m_thread.GetProcess()); + + if (!ret_value) + { + // See if we've just stepped to another part of the same line number... + StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get(); + + SymbolContext new_context(frame->GetSymbolContext(eSymbolContextEverything)); + if (m_addr_context.line_entry.IsValid() && new_context.line_entry.IsValid()) + { + if ((m_addr_context.line_entry.file == new_context.line_entry.file) + && (m_addr_context.line_entry.line == new_context.line_entry.line)) + { + m_addr_context = new_context; + m_address_range = m_addr_context.line_entry.range; + ret_value = true; + if (log) + { + StreamString s; + m_address_range.Dump (&s, &m_thread.GetProcess(), Address::DumpStyleLoadAddress); + + log->Printf ("Step range plan stepped to another range of same line: %s", s.GetData()); + } + } + } + + } + + if (!ret_value && log) + log->Printf ("Step range plan out of range to 0x%llx", pc_load_addr); + + return ret_value; +} + +bool +ThreadPlanStepRange::InSymbol() +{ + lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); + Process *process = m_thread.CalculateProcess(); + + if (m_addr_context.function != NULL) + { + return m_addr_context.function->GetAddressRange().ContainsLoadAddress (cur_pc, process); + } + else if (m_addr_context.symbol != NULL) + { + return m_addr_context.symbol->GetAddressRangeRef().ContainsLoadAddress (cur_pc, process); + } + return false; +} + +// FIXME: This should also handle inlining if we aren't going to do inlining in the +// main stack. +// +// Ideally we should remember the whole stack frame list, and then compare that +// to the current list. + +bool +ThreadPlanStepRange::FrameIsYounger () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + uint32_t current_depth = m_thread.GetStackFrameCount(); + if (current_depth == m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsYounger still in start function."); + return false; + } + else if (current_depth < m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsYounger stepped out: start depth: %d current depth %d.", m_stack_depth, current_depth); + return false; + } + else + { + if (log) + log->Printf ("Step range FrameIsYounger stepped in: start depth: %d current depth %d.", m_stack_depth, current_depth); + return true; + } +} + +bool +ThreadPlanStepRange::FrameIsOlder () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + uint32_t current_depth = m_thread.GetStackFrameCount(); + if (current_depth == m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsOlder still in start function."); + return false; + } + else if (current_depth < m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsOlder stepped out: start depth: %d current depth %d.", m_stack_depth, current_depth); + return true; + } + else + { + if (log) + log->Printf ("Step range FrameIsOlder stepped in: start depth: %d current depth %d.", m_stack_depth, current_depth); + return false; + } +} + +bool +ThreadPlanStepRange::StopOthers () +{ + if (m_stop_others == lldb::eOnlyThisThread + || m_stop_others == lldb::eOnlyDuringStepping) + return true; + else + return false; +} + +bool +ThreadPlanStepRange::WillStop () +{ + return true; +} + +StateType +ThreadPlanStepRange::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepRange::MischiefManaged () +{ + bool done = true; + if (!IsPlanComplete()) + { + if (InRange()) + { + done = false; + } + else if (!FrameIsOlder()) + { + if (m_no_more_plans) + done = true; + else + done = false; + } + else + done = true; + } + + if (done) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step through range plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } + +} diff --git a/lldb/source/Target/ThreadPlanStepThrough.cpp b/lldb/source/Target/ThreadPlanStepThrough.cpp new file mode 100644 index 000000000000..5e614e5aa52e --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepThrough.cpp @@ -0,0 +1,137 @@ +//===-- ThreadPlanStepThrough.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/Target/ThreadPlanStepThrough.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepThrough: If the current instruction is a trampoline, step through it +// If it is the beginning of the prologue of a function, step through that as well. +// FIXME: At present only handles DYLD trampolines. +//---------------------------------------------------------------------- + +ThreadPlanStepThrough::ThreadPlanStepThrough (Thread &thread, bool stop_others) : + ThreadPlan ("Step through trampolines and prologues", thread, eVoteNoOpinion, eVoteNoOpinion), + m_start_address (0), + m_stop_others (stop_others) +{ + m_start_address = GetThread().GetRegisterContext()->GetPC(0); +} + +ThreadPlanStepThrough::~ThreadPlanStepThrough () +{ +} + +void +ThreadPlanStepThrough::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("Step through"); + else + { + s->Printf ("Stepping through trampoline code from: "); + s->Address(m_start_address, sizeof (addr_t)); + } +} + +bool +ThreadPlanStepThrough::ValidatePlan (Stream *error) +{ + if (HappyToStopHere()) + return false; + else + return true; +} + +bool +ThreadPlanStepThrough::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanStepThrough::ShouldStop (Event *event_ptr) +{ + return true; +} + +bool +ThreadPlanStepThrough::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepThrough::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepThrough::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume(resume_state, current_plan); + if (current_plan) + { + ThreadPlanSP sub_plan_sp(m_thread.GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others)); + if (sub_plan_sp != NULL) + PushPlan (sub_plan_sp); + } + return true; +} + +bool +ThreadPlanStepThrough::WillStop () +{ + return true; +} + +bool +ThreadPlanStepThrough::MischiefManaged () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + // Stop if we're happy with the place we've landed... + + if (!HappyToStopHere()) + { + // If we are still at the PC we were trying to step over. + return false; + } + else + { + if (log) + log->Printf("Completed step through step plan."); + ThreadPlan::MischiefManaged (); + return true; + } +} + +bool +ThreadPlanStepThrough::HappyToStopHere() +{ + // This should again ask the various trampolines whether we are still at a + // trampoline point, and if so, continue through the possibly nested trampolines. + + return true; +} + diff --git a/lldb/source/Target/ThreadPlanStepUntil.cpp b/lldb/source/Target/ThreadPlanStepUntil.cpp new file mode 100644 index 000000000000..3f9cdb550470 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepUntil.cpp @@ -0,0 +1,360 @@ +//===-- ThreadPlanStepUntil.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//m_should_stop + +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepUntil.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepUntil: Run until we reach a given line number or step out of the current frame +//---------------------------------------------------------------------- + +ThreadPlanStepUntil::ThreadPlanStepUntil +( + Thread &thread, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others +) : + ThreadPlan ("Step out", thread, eVoteNoOpinion, eVoteNoOpinion), + m_step_from_insn (LLDB_INVALID_ADDRESS), + m_return_addr (LLDB_INVALID_ADDRESS), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_stepped_out(false), + m_should_stop(false), + m_explains_stop(false), + m_ran_analyze (false), + m_stop_others (stop_others) +{ + + SetOkayToDiscard(true); + // Stash away our "until" addresses: + Target &target = m_thread.GetProcess().GetTarget(); + + m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); + lldb::user_id_t thread_id = m_thread.GetID(); + + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + m_return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + Breakpoint *return_bp = target.CreateBreakpoint (m_return_addr, true).get(); + if (return_bp != NULL) + { + return_bp->SetThreadID(thread_id); + m_return_bp_id = return_bp->GetID(); + } + else + { + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + + m_stack_depth = m_thread.GetStackFrameCount(); + + // Now set breakpoints on all our return addresses: + for (int i = 0; i < num_addresses; i++) + { + Breakpoint *until_bp = target.CreateBreakpoint (address_list[i], true).get(); + if (until_bp != NULL) + { + until_bp->SetThreadID(thread_id); + m_until_points[address_list[i]] = until_bp->GetID(); + } + else + { + m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID; + } + } +} + +ThreadPlanStepUntil::~ThreadPlanStepUntil () +{ + Clear(); +} + +void +ThreadPlanStepUntil::Clear() +{ + Target &target = m_thread.GetProcess().GetTarget(); + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) + { + target.RemoveBreakpointByID(m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + target.RemoveBreakpointByID((*pos).second); + } + m_until_points.clear(); +} + +void +ThreadPlanStepUntil::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf ("step until"); + if (m_stepped_out) + s->Printf (" - stepped out"); + } + else + { + if (m_until_points.size() == 1) + s->Printf ("Stepping from address 0x%llx until we reach 0x%llx using breakpoint %d", + (uint64_t)m_step_from_insn, + (uint64_t) (*m_until_points.begin()).first, + (*m_until_points.begin()).second); + else + { + until_collection::iterator pos, end = m_until_points.end(); + s->Printf ("Stepping from address 0x%llx until we reach one of:", + (uint64_t)m_step_from_insn); + for (pos = m_until_points.begin(); pos != end; pos++) + { + s->Printf ("\n\t0x%llx (bp: %d)", (uint64_t) (*pos).first, (*pos).second); + } + } + s->Printf(" stepped out address is 0x%lx.", (uint64_t) m_return_addr); + } +} + +bool +ThreadPlanStepUntil::ValidatePlan (Stream *error) +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + { + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + if (!LLDB_BREAK_ID_IS_VALID ((*pos).second)) + return false; + } + return true; + } +} + +void +ThreadPlanStepUntil::AnalyzeStop() +{ + if (m_ran_analyze) + return; + + Thread::StopInfo info; + m_should_stop = true; + m_explains_stop = false; + + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + { + // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened... + BreakpointSiteSP this_site = m_thread.GetProcess().GetBreakpointSiteList().FindByID (info.GetBreakpointSiteID()); + if (!this_site) + { + m_explains_stop = false; + return; + } + + if (this_site->IsBreakpointAtThisSite (m_return_bp_id)) + { + // If we are at our "step out" breakpoint, and the stack depth has shrunk, then + // this is indeed our stop. + // If the stack depth has grown, then we've hit our step out breakpoint recursively. + // If we are the only breakpoint at that location, then we do explain the stop, and + // we'll just continue. + // If there was another breakpoint here, then we don't explain the stop, but we won't + // mark ourselves Completed, because maybe that breakpoint will continue, and then + // we'll finish the "until". + if (m_stack_depth > m_thread.GetStackFrameCount()) + { + m_stepped_out = true; + SetPlanComplete(); + } + else + m_should_stop = false; + + if (this_site->GetNumberOfOwners() == 1) + m_explains_stop = true; + else + m_explains_stop = false; + return; + } + else + { + // Check if we've hit one of our "until" breakpoints. + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + if (this_site->IsBreakpointAtThisSite ((*pos).second)) + { + // If we're at the right stack depth, then we're done. + if (m_stack_depth == m_thread.GetStackFrameCount()) + SetPlanComplete(); + else + m_should_stop = false; + + // Otherwise we've hit this breakpoint recursively. If we're the + // only breakpoint here, then we do explain the stop, and we'll continue. + // If not then we should let higher plans handle this stop. + if (this_site->GetNumberOfOwners() == 1) + m_explains_stop = true; + else + { + m_should_stop = true; + m_explains_stop = false; + } + return; + } + } + } + // If we get here we haven't hit any of our breakpoints, so let the higher + // plans take care of the stop. + m_explains_stop = false; + return; + } + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + m_explains_stop = false; + break; + default: + m_explains_stop = true; + break; + } + } +} + +bool +ThreadPlanStepUntil::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + AnalyzeStop(); + return m_explains_stop; +} + +bool +ThreadPlanStepUntil::ShouldStop (Event *event_ptr) +{ + // If we've told our self in ExplainsStop that we plan to continue, then + // do so here. Otherwise, as long as this thread has stopped for a reason, + // we will stop. + + Thread::StopInfo stop_info (&m_thread); + if (!m_thread.GetStopInfo(&stop_info) + || stop_info.GetStopReason() == eStopReasonNone) + return false; + + AnalyzeStop(); + return m_should_stop; +} + +bool +ThreadPlanStepUntil::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepUntil::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepUntil::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (current_plan) + { + Target &target = m_thread.GetProcess().GetTarget(); + Breakpoint *return_bp = target.GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (true); + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + Breakpoint *until_bp = target.GetBreakpointByID((*pos).second).get(); + if (until_bp != NULL) + until_bp->SetEnabled (true); + } + } + + m_should_stop = true; + m_ran_analyze = false; + m_explains_stop = false; + return true; +} + +bool +ThreadPlanStepUntil::WillStop () +{ + Target &target = m_thread.GetProcess().GetTarget(); + Breakpoint *return_bp = target.GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (false); + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + Breakpoint *until_bp = target.GetBreakpointByID((*pos).second).get(); + if (until_bp != NULL) + until_bp->SetEnabled (false); + } + return true; +} + +bool +ThreadPlanStepUntil::MischiefManaged () +{ + + // I'm letting "PlanExplainsStop" do all the work, and just reporting that here. + bool done = false; + if (IsPlanComplete()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step until plan."); + + Clear(); + done = true; + } + if (done) + ThreadPlan::MischiefManaged (); + + return done; + +} + diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp new file mode 100644 index 000000000000..e6500c5dba83 --- /dev/null +++ b/lldb/source/Target/UnixSignals.cpp @@ -0,0 +1,310 @@ +//===-- UnixSignals.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/Target/UnixSignals.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +UnixSignals::Signal::Signal (const char *name, bool default_suppress, bool default_stop, bool default_notify) : + m_name (name), + m_conditions () +{ + m_conditions[Signal::eCondSuppress] = default_suppress; + m_conditions[Signal::eCondStop] = default_stop; + m_conditions[Signal::eCondNotify] = default_notify; +} + +//---------------------------------------------------------------------- +// UnixSignals constructor +//---------------------------------------------------------------------- +UnixSignals::UnixSignals () +{ + Reset (); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +UnixSignals::~UnixSignals () +{ +} + +void +UnixSignals::Reset () +{ + // This builds one standard set of Unix Signals. If yours aren't quite in this + // order, you can either subclass this class, and use Add & Remove to change them + // or you can subclass and build them afresh in your constructor; + m_signals.clear(); + + AddSignal(1, "SIGHUP", false, true, true ); // 1 hangup + AddSignal(2, "SIGINT", true, true, true ); // 2 interrupt + AddSignal(3, "SIGQUIT", false, true, true ); // 3 quit + AddSignal(4, "SIGILL", false, true, true ); // 4 illegal instruction (not reset when caught) + AddSignal(5, "SIGTRAP", true, true, true ); // 5 trace trap (not reset when caught) + AddSignal(6, "SIGABRT", false, true, true ); // 6 abort() + AddSignal(7, "SIGEMT", false, true, true ); // 7 pollable event ([XSR] generated, not supported) + AddSignal(8, "SIGFPE", false, true, true ); // 8 floating point exception + AddSignal(9, "SIGKILL", false, true, true ); // 9 kill (cannot be caught or ignored) + AddSignal(10, "SIGBUS", false, true, true ); // 10 bus error + AddSignal(11, "SIGSEGV", false, true, true ); // 11 segmentation violation + AddSignal(12, "SIGSYS", false, true, true ); // 12 bad argument to system call + AddSignal(13, "SIGPIPE", false, true, true ); // 13 write on a pipe with no one to read it + AddSignal(14, "SIGALRM", false, false, true ); // 14 alarm clock + AddSignal(15, "SIGTERM", false, true, true ); // 15 software termination signal from kill + AddSignal(16, "SIGURG", false, false, false); // 16 urgent condition on IO channel + AddSignal(17, "SIGSTOP", false, true, true ); // 17 sendable stop signal not from tty + AddSignal(18, "SIGTSTP", false, true, true ); // 18 stop signal from tty + AddSignal(19, "SIGCONT", false, true, true ); // 19 continue a stopped process + AddSignal(20, "SIGCHLD", false, false, true ); // 20 to parent on child stop or exit + AddSignal(21, "SIGTTIN", false, true, true ); // 21 to readers pgrp upon background tty read + AddSignal(22, "SIGTTOU", false, true, true ); // 22 like TTIN for output if (tp->t_local<OSTOP) + AddSignal(23, "SIGIO", false, false, false); // 23 input/output possible signal + AddSignal(24, "SIGXCPU", false, true, true ); // 24 exceeded CPU time limit + AddSignal(25, "SIGXFSZ", false, true, true ); // 25 exceeded file size limit + AddSignal(26, "SIGVTALRM", false, false, false); // 26 virtual time alarm + AddSignal(27, "SIGPROF", false, false, false); // 27 profiling time alarm + AddSignal(28, "SIGWINCH", false, false, false); // 28 window size changes + AddSignal(29, "SIGINFO", false, true, true ); // 29 information request + AddSignal(30, "SIGUSR1", false, true, true ); // 30 user defined signal 1 + AddSignal(31, "SIGUSR2", false, true, true ); // 31 user defined signal 2 +} +void +UnixSignals::AddSignal (int signo, const char *name, bool default_suppress, bool default_stop, bool default_notify) +{ + collection::iterator iter = m_signals.find (signo); + struct Signal new_signal (name, default_suppress, default_stop, default_notify); + + if (iter != m_signals.end()) + m_signals.erase (iter); + + m_signals.insert (iter, collection::value_type (signo, new_signal)); +} + +void +UnixSignals::RemoveSignal (int signo) +{ + collection::iterator pos = m_signals.find (signo); + if (pos != m_signals.end()) + m_signals.erase (pos); +} + +UnixSignals::Signal * +UnixSignals::GetSignalByName (const char *name, int32_t &signo) +{ + ConstString const_name (name); + + collection::iterator pos, end = m_signals.end (); + for (pos = m_signals.begin (); pos != end; pos++) + { + if (const_name == (*pos).second.m_name) + { + signo = (*pos).first; + return &((*pos).second); + } + } + return NULL; +} + + +const UnixSignals::Signal * +UnixSignals::GetSignalByName (const char *name, int32_t &signo) const +{ + ConstString const_name (name); + + collection::const_iterator pos, end = m_signals.end (); + for (pos = m_signals.begin (); pos != end; pos++) + { + if (const_name == (*pos).second.m_name) + { + signo = (*pos).first; + return &((*pos).second); + } + } + return NULL; +} + +const char * +UnixSignals::GetSignalAsCString (int signo) const +{ + collection::const_iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return NULL; + else + return (*pos).second.m_name.GetCString (); +} + + +bool +UnixSignals::SignalIsValid (int32_t signo) const +{ + return m_signals.find (signo) != m_signals.end(); +} + + +int32_t +UnixSignals::GetSignalNumberFromName (const char *name) const +{ + int32_t signo; + const Signal *signal = GetSignalByName (name, signo); + if (signal == NULL) + return LLDB_INVALID_SIGNAL_NUMBER; + else + return signo; +} + +int32_t +UnixSignals::GetFirstSignalNumber () const +{ + if (m_signals.empty()) + return LLDB_INVALID_SIGNAL_NUMBER; + + return (*m_signals.begin ()).first; +} + +int32_t +UnixSignals::GetNextSignalNumber (int32_t current_signal) const +{ + collection::const_iterator pos = m_signals.find (current_signal); + collection::const_iterator end = m_signals.end(); + if (pos == end) + return LLDB_INVALID_SIGNAL_NUMBER; + else + { + pos++; + if (pos == end) + return LLDB_INVALID_SIGNAL_NUMBER; + else + return (*pos).first; + } +} + +const char * +UnixSignals::GetSignalInfo +( + int32_t signo, + bool &should_suppress, + bool &should_stop, + bool &should_notify +) const +{ + collection::const_iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return NULL; + else + { + const Signal &signal = (*pos).second; + should_suppress = signal.m_conditions[Signal::eCondSuppress]; + should_stop = signal.m_conditions[Signal::eCondStop]; + should_notify = signal.m_conditions[Signal::eCondNotify]; + return signal.m_name.AsCString(""); + } +} + +bool +UnixSignals::GetCondition +( + int32_t signo, + UnixSignals::Signal::Condition cond_pos +) const +{ + collection::const_iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return false; + else + return (*pos).second.m_conditions[cond_pos]; +} + +bool +UnixSignals::SetCondition (int32_t signo, UnixSignals::Signal::Condition cond_pos, bool value) +{ + collection::iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return false; + else + { + bool ret_value = (*pos).second.m_conditions[cond_pos]; + (*pos).second.m_conditions[cond_pos] = value; + return ret_value; + } +} + +bool +UnixSignals::SetCondition (const char *signal_name, UnixSignals::Signal::Condition cond_pos, bool value) +{ + int32_t signo; + Signal *signal = GetSignalByName (signal_name, signo); + if (signal == NULL) + return false; + else + { + bool ret_value = signal->m_conditions[cond_pos]; + signal->m_conditions[cond_pos] = value; + return ret_value; + } +} + +bool +UnixSignals::GetShouldSuppress (int signo) const +{ + return GetCondition (signo, Signal::eCondSuppress); +} + +bool +UnixSignals::SetShouldSuppress (int signo, bool value) +{ + return SetCondition (signo, Signal::eCondSuppress, value); +} + +bool +UnixSignals::SetShouldSuppress (const char *signal_name, bool value) +{ + return SetCondition (signal_name, Signal::eCondSuppress, value); +} + +bool +UnixSignals::GetShouldStop (int signo) const +{ + return GetCondition (signo, Signal::eCondStop); +} + +bool +UnixSignals::SetShouldStop (int signo, bool value) +{ + return SetCondition (signo, Signal::eCondStop, value); +} + +bool +UnixSignals::SetShouldStop (const char *signal_name, bool value) +{ + return SetCondition (signal_name, Signal::eCondStop, value); +} + +bool +UnixSignals::GetShouldNotify (int signo) const +{ + return GetCondition (signo, Signal::eCondNotify); +} + +bool +UnixSignals::SetShouldNotify (int signo, bool value) +{ + return SetCondition (signo, Signal::eCondNotify, value); +} + +bool +UnixSignals::SetShouldNotify (const char *signal_name, bool value) +{ + return SetCondition (signal_name, Signal::eCondNotify, value); +} diff --git a/lldb/source/Utility/ARM_DWARF_Registers.h b/lldb/source/Utility/ARM_DWARF_Registers.h new file mode 100644 index 000000000000..40b973be6daf --- /dev/null +++ b/lldb/source/Utility/ARM_DWARF_Registers.h @@ -0,0 +1,190 @@ +//===-- ARM_DWARF_Registers.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_ARM_DWARF_Registers_h_ +#define utility_ARM_DWARF_Registers_h_ + +enum +{ + dwarf_r0 = 0, + dwarf_r1, + dwarf_r2, + dwarf_r3, + dwarf_r4, + dwarf_r5, + dwarf_r6, + dwarf_r7, + dwarf_r8, + dwarf_r9, + dwarf_r10, + dwarf_r11, + dwarf_r12, + dwarf_sp, + dwarf_lr, + dwarf_pc, + dwarf_cpsr, + + dwarf_s0 = 64, + dwarf_s1, + dwarf_s2, + dwarf_s3, + dwarf_s4, + dwarf_s5, + dwarf_s6, + dwarf_s7, + dwarf_s8, + dwarf_s9, + dwarf_s10, + dwarf_s11, + dwarf_s12, + dwarf_s13, + dwarf_s14, + dwarf_s15, + dwarf_s16, + dwarf_s17, + dwarf_s18, + dwarf_s19, + dwarf_s20, + dwarf_s21, + dwarf_s22, + dwarf_s23, + dwarf_s24, + dwarf_s25, + dwarf_s26, + dwarf_s27, + dwarf_s28, + dwarf_s29, + dwarf_s30, + dwarf_s31, + + // FPA Registers 0-7 + dwarf_f0 = 96, + dwarf_f1, + dwarf_f2, + dwarf_f3, + dwarf_f4, + dwarf_f5, + dwarf_f6, + dwarf_f7, + + // Intel wireless MMX general purpose registers 0–7 + dwarf_wCGR0 = 104, + dwarf_wCGR1, + dwarf_wCGR2, + dwarf_wCGR3, + dwarf_wCGR4, + dwarf_wCGR5, + dwarf_wCGR6, + dwarf_wCGR7, + + // XScale accumulator register 0–7 (they do overlap with wCGR0 - wCGR7) + dwarf_ACC0 = 104, + dwarf_ACC1, + dwarf_ACC2, + dwarf_ACC3, + dwarf_ACC4, + dwarf_ACC5, + dwarf_ACC6, + dwarf_ACC7, + + // Intel wireless MMX data registers 0–15 + dwarf_wR0 = 112, + dwarf_wR1, + dwarf_wR2, + dwarf_wR3, + dwarf_wR4, + dwarf_wR5, + dwarf_wR6, + dwarf_wR7, + dwarf_wR8, + dwarf_wR9, + dwarf_wR10, + dwarf_wR11, + dwarf_wR12, + dwarf_wR13, + dwarf_wR14, + dwarf_wR15, + + dwarf_spsr = 128, + dwarf_spsr_fiq, + dwarf_spsr_irq, + dwarf_spsr_abt, + dwarf_spsr_und, + dwarf_spsr_svc, + + dwarf_r8_usr = 144, + dwarf_r9_usr, + dwarf_r10_usr, + dwarf_r11_usr, + dwarf_r12_usr, + dwarf_r13_usr, + dwarf_r14_usr, + dwarf_r8_fiq, + dwarf_r9_fiq, + dwarf_r10_fiq, + dwarf_r11_fiq, + dwarf_r12_fiq, + dwarf_r13_fiq, + dwarf_r14_fiq, + dwarf_r13_irq, + dwarf_r14_irq, + dwarf_r13_abt, + dwarf_r14_abt, + dwarf_r13_und, + dwarf_r14_und, + dwarf_r13_svc, + dwarf_r14_svc, + + // Intel wireless MMX control register in co-processor 0–7 + dwarf_wC0 = 192, + dwarf_wC1, + dwarf_wC2, + dwarf_wC3, + dwarf_wC4, + dwarf_wC5, + dwarf_wC6, + dwarf_wC7, + + // VFP-v3/Neon + dwarf_d0 = 256, + dwarf_d1, + dwarf_d2, + dwarf_d3, + dwarf_d4, + dwarf_d5, + dwarf_d6, + dwarf_d7, + dwarf_d8, + dwarf_d9, + dwarf_d10, + dwarf_d11, + dwarf_d12, + dwarf_d13, + dwarf_d14, + dwarf_d15, + dwarf_d16, + dwarf_d17, + dwarf_d18, + dwarf_d19, + dwarf_d20, + dwarf_d21, + dwarf_d22, + dwarf_d23, + dwarf_d24, + dwarf_d25, + dwarf_d26, + dwarf_d27, + dwarf_d28, + dwarf_d29, + dwarf_d30, + dwarf_d31 +}; + +#endif utility_ARM_DWARF_Registers_h_ + diff --git a/lldb/source/Utility/ARM_GCC_Registers.h b/lldb/source/Utility/ARM_GCC_Registers.h new file mode 100644 index 000000000000..fe1327b5e14c --- /dev/null +++ b/lldb/source/Utility/ARM_GCC_Registers.h @@ -0,0 +1,35 @@ +//===-- ARM_GCC_Registers.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_ARM_GCC_Registers_h_ +#define utility_ARM_GCC_Registers_h_ + +enum +{ + gcc_r0 = 0, + gcc_r1, + gcc_r2, + gcc_r3, + gcc_r4, + gcc_r5, + gcc_r6, + gcc_r7, + gcc_r8, + gcc_r9, + gcc_r10, + gcc_r11, + gcc_r12, + gcc_sp, + gcc_lr, + gcc_pc, + gcc_cpsr +}; + +#endif utility_ARM_GCC_Registers_h_ + diff --git a/lldb/source/Utility/PseudoTerminal.cpp b/lldb/source/Utility/PseudoTerminal.cpp new file mode 100644 index 000000000000..9bbecae6e6d6 --- /dev/null +++ b/lldb/source/Utility/PseudoTerminal.cpp @@ -0,0 +1,336 @@ +//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PseudoTerminal.h" + +#include +#include +#include +#include + +using namespace lldb_utility; + +//---------------------------------------------------------------------- +// PseudoTerminal constructor +//---------------------------------------------------------------------- +PseudoTerminal::PseudoTerminal () : + m_master_fd(invalid_fd), + m_slave_fd(invalid_fd) +{ +} + +//---------------------------------------------------------------------- +// Destructor +// +// The destructor will close the master and slave file descriptors +// if they are valid and ownwership has not been released using the +// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor() +// member functions. +//---------------------------------------------------------------------- +PseudoTerminal::~PseudoTerminal () +{ + CloseMasterFileDescriptor(); + CloseSlaveFileDescriptor(); +} + +//---------------------------------------------------------------------- +// Close the master file descriptor if it is valid. +//---------------------------------------------------------------------- +void +PseudoTerminal::CloseMasterFileDescriptor () +{ + if (m_master_fd >= 0) + { + ::close (m_master_fd); + m_master_fd = invalid_fd; + } +} + +//---------------------------------------------------------------------- +// Close the slave file descriptor if it is valid. +//---------------------------------------------------------------------- +void +PseudoTerminal::CloseSlaveFileDescriptor () +{ + if (m_slave_fd >= 0) + { + ::close (m_slave_fd); + m_slave_fd = invalid_fd; + } +} + +//---------------------------------------------------------------------- +// Open the first available pseudo terminal with OFLAG as the +// permissions. The file descriptor is stored in this object and can +// be accessed with the MasterFileDescriptor() accessor. The +// ownership of the master file descriptor can be released using +// the ReleaseMasterFileDescriptor() accessor. If this object has +// a valid master files descriptor when its destructor is called, it +// will close the master file descriptor, therefore clients must +// call ReleaseMasterFileDescriptor() if they wish to use the master +// file descriptor after this object is out of scope or destroyed. +// +// RETURNS: +// Zero when successful, non-zero indicating an error occurred. +//---------------------------------------------------------------------- +bool +PseudoTerminal::OpenFirstAvailableMaster (int oflag, char *error_str, size_t error_len) +{ + if (error_str) + error_str[0] = '\0'; + + // Open the master side of a pseudo terminal + m_master_fd = ::posix_openpt (oflag); + if (m_master_fd < 0) + { + if (error_str) + ::strerror_r (errno, error_str, error_len); + return false; + } + + // Grant access to the slave pseudo terminal + if (::grantpt (m_master_fd) < 0) + { + if (error_str) + ::strerror_r (errno, error_str, error_len); + CloseMasterFileDescriptor (); + return false; + } + + // Clear the lock flag on the slave pseudo terminal + if (::unlockpt (m_master_fd) < 0) + { + if (error_str) + ::strerror_r (errno, error_str, error_len); + CloseMasterFileDescriptor (); + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +// Open the slave pseudo terminal for the current master pseudo +// terminal. A master pseudo terminal should already be valid prior to +// calling this function (see OpenFirstAvailableMaster()). +// The file descriptor is stored this object's member variables and can +// be accessed via the GetSlaveFileDescriptor(), or released using the +// ReleaseSlaveFileDescriptor() member function. +// +// RETURNS: +// Zero when successful, non-zero indicating an error occurred. +//---------------------------------------------------------------------- +bool +PseudoTerminal::OpenSlave (int oflag, char *error_str, size_t error_len) +{ + if (error_str) + error_str[0] = '\0'; + + CloseSlaveFileDescriptor(); + + // Open the master side of a pseudo terminal + const char *slave_name = GetSlaveName (error_str, error_len); + + if (slave_name == NULL) + return false; + + m_slave_fd = ::open (slave_name, oflag); + + if (m_slave_fd < 0) + { + if (error_str) + ::strerror_r (errno, error_str, error_len); + return false; + } + + return true; +} + + + +//---------------------------------------------------------------------- +// Get the name of the slave pseudo terminal. A master pseudo terminal +// should already be valid prior to calling this function (see +// OpenFirstAvailableMaster()). +// +// RETURNS: +// NULL if no valid master pseudo terminal or if ptsname() fails. +// The name of the slave pseudo terminal as a NULL terminated C string +// that comes from static memory, so a copy of the string should be +// made as subsequent calls can change this value. +//---------------------------------------------------------------------- +const char* +PseudoTerminal::GetSlaveName (char *error_str, size_t error_len) const +{ + if (error_str) + error_str[0] = '\0'; + + if (m_master_fd < 0) + { + if (error_str) + ::snprintf (error_str, error_len, "%s", "master file descriptor is invalid"); + return NULL; + } + const char *slave_name = ::ptsname (m_master_fd); + + if (error_str && slave_name == NULL) + ::strerror_r (errno, error_str, error_len); + + return slave_name; +} + + +//---------------------------------------------------------------------- +// Fork a child process and have its stdio routed to a pseudo terminal. +// +// In the parent process when a valid pid is returned, the master file +// descriptor can be used as a read/write access to stdio of the +// child process. +// +// In the child process the stdin/stdout/stderr will already be routed +// to the slave pseudo terminal and the master file descriptor will be +// closed as it is no longer needed by the child process. +// +// This class will close the file descriptors for the master/slave +// when the destructor is called, so be sure to call +// ReleaseMasterFileDescriptor() or ReleaseSlaveFileDescriptor() if any +// file descriptors are going to be used past the lifespan of this +// object. +// +// RETURNS: +// in the parent process: the pid of the child, or -1 if fork fails +// in the child process: zero +//---------------------------------------------------------------------- +lldb::pid_t +PseudoTerminal::Fork (char *error_str, size_t error_len) +{ + if (error_str) + error_str[0] = '\0'; + + pid_t pid = LLDB_INVALID_PROCESS_ID; + if (OpenFirstAvailableMaster (O_RDWR, error_str, error_len)) + { + // Successfully opened our master pseudo terminal + + pid = ::fork (); + if (pid < 0) + { + // Fork failed + if (error_str) + ::strerror_r (errno, error_str, error_len); + } + else if (pid == 0) + { + // Child Process + ::setsid(); + + if (OpenSlave (O_RDWR, error_str, error_len)) + { + // Successfully opened slave + // We are done with the master in the child process so lets close it + CloseMasterFileDescriptor (); + +#if defined (TIOCSCTTY) + // Acquire the controlling terminal + if (::ioctl (m_slave_fd, TIOCSCTTY, (char *)0) < 0) + { + if (error_str) + ::strerror_r (errno, error_str, error_len); + } +#endif + // Duplicate all stdio file descriptors to the slave pseudo terminal + if (::dup2 (m_slave_fd, STDIN_FILENO) != STDIN_FILENO) + { + if (error_str && !error_str[0]) + ::strerror_r (errno, error_str, error_len); + } + + if (::dup2 (m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) + { + if (error_str && !error_str[0]) + ::strerror_r (errno, error_str, error_len); + } + + if (::dup2 (m_slave_fd, STDERR_FILENO) != STDERR_FILENO) + { + if (error_str && !error_str[0]) + ::strerror_r (errno, error_str, error_len); + } + } + } + else + { + // Parent Process + // Do nothing and let the pid get returned! + } + } + return pid; +} + +//---------------------------------------------------------------------- +// The master file descriptor accessor. This object retains ownership +// of the master file descriptor when this accessor is used. Use +// ReleaseMasterFileDescriptor() if you wish this object to release +// ownership of the master file descriptor. +// +// Returns the master file descriptor, or -1 if the master file +// descriptor is not currently valid. +//---------------------------------------------------------------------- +int +PseudoTerminal::GetMasterFileDescriptor () const +{ + return m_master_fd; +} + +//---------------------------------------------------------------------- +// The slave file descriptor accessor. +// +// Returns the slave file descriptor, or -1 if the slave file +// descriptor is not currently valid. +//---------------------------------------------------------------------- +int +PseudoTerminal::GetSlaveFileDescriptor () const +{ + return m_slave_fd; +} + +//---------------------------------------------------------------------- +// Release ownership of the master pseudo terminal file descriptor +// without closing it. The destructor for this class will close the +// master file descriptor if the ownership isn't released using this +// call and the master file descriptor has been opened. +//---------------------------------------------------------------------- +int +PseudoTerminal::ReleaseMasterFileDescriptor () +{ + // Release ownership of the master pseudo terminal file + // descriptor without closing it. (the destructor for this + // class will close it otherwise!) + int fd = m_master_fd; + m_master_fd = invalid_fd; + return fd; +} + +//---------------------------------------------------------------------- +// Release ownership of the slave pseudo terminal file descriptor +// without closing it. The destructor for this class will close the +// slave file descriptor if the ownership isn't released using this +// call and the slave file descriptor has been opened. +//---------------------------------------------------------------------- +int +PseudoTerminal::ReleaseSlaveFileDescriptor () +{ + // Release ownership of the slave pseudo terminal file + // descriptor without closing it (the destructor for this + // class will close it otherwise!) + int fd = m_slave_fd; + m_slave_fd = invalid_fd; + return fd; +} + diff --git a/lldb/source/Utility/PseudoTerminal.h b/lldb/source/Utility/PseudoTerminal.h new file mode 100644 index 000000000000..5d680745a325 --- /dev/null +++ b/lldb/source/Utility/PseudoTerminal.h @@ -0,0 +1,267 @@ +//===-- 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 + +#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. + //------------------------------------------------------------------ + 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/lldb/source/Utility/StringExtractor.cpp b/lldb/source/Utility/StringExtractor.cpp new file mode 100644 index 000000000000..d2f69f95b7c6 --- /dev/null +++ b/lldb/source/Utility/StringExtractor.cpp @@ -0,0 +1,360 @@ +//===-- StringExtractor.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StringExtractor.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +static inline int +xdigit_to_sint (char ch) +{ + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + return ch - '0'; +} + +static inline unsigned int +xdigit_to_uint (uint8_t ch) +{ + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return 10u + ch - 'a'; + return ch - '0'; +} + +//---------------------------------------------------------------------- +// StringExtractor constructor +//---------------------------------------------------------------------- +StringExtractor::StringExtractor() : + m_packet(), + m_index (0) +{ +} + + +StringExtractor::StringExtractor(const char *packet_cstr) : + m_packet(), + m_index (0) +{ + if (packet_cstr) + m_packet.assign (packet_cstr); +} + + +//---------------------------------------------------------------------- +// StringExtractor copy constructor +//---------------------------------------------------------------------- +StringExtractor::StringExtractor(const StringExtractor& rhs) : + m_packet (rhs.m_packet), + m_index (rhs.m_index) +{ + +} + +//---------------------------------------------------------------------- +// StringExtractor assignment operator +//---------------------------------------------------------------------- +const StringExtractor& +StringExtractor::operator=(const StringExtractor& rhs) +{ + if (this != &rhs) + { + m_packet = rhs.m_packet; + m_index = rhs.m_index; + + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StringExtractor::~StringExtractor() +{ +} + + +char +StringExtractor::GetChar (char fail_value) +{ + if (m_index < m_packet.size()) + { + char ch = m_packet[m_index]; + ++m_index; + return ch; + } + m_index = UINT32_MAX; + return fail_value; +} + +uint32_t +StringExtractor::GetNumHexASCIICharsAtFilePos (uint32_t max) const +{ + uint32_t idx = m_index; + const size_t size = m_packet.size(); + while (idx < size && idx - m_index < max && isxdigit(m_packet[idx])) + ++idx; + return idx - m_index; +} +//---------------------------------------------------------------------- +// Extract a signed character from two hex ASCII chars in the packet +// string +//---------------------------------------------------------------------- +int8_t +StringExtractor::GetHexS8 (int8_t fail_value) +{ + if (GetNumHexASCIICharsAtFilePos(2)) + { + char hi_nibble_char = m_packet[m_index]; + char lo_nibble_char = m_packet[m_index+1]; + + if (isxdigit(hi_nibble_char) && isxdigit(lo_nibble_char)) + { + char hi_nibble = xdigit_to_sint (hi_nibble_char); + char lo_nibble = xdigit_to_sint (lo_nibble_char); + m_index += 2; + return (hi_nibble << 4) + lo_nibble; + } + } + m_index = UINT32_MAX; + return fail_value; +} + +//---------------------------------------------------------------------- +// Extract an unsigned character from two hex ASCII chars in the packet +// string +//---------------------------------------------------------------------- +uint8_t +StringExtractor::GetHexU8 (uint8_t fail_value) +{ + if (GetNumHexASCIICharsAtFilePos(2)) + { + uint8_t hi_nibble_char = m_packet[m_index]; + uint8_t lo_nibble_char = m_packet[m_index+1]; + + if (isxdigit(hi_nibble_char) && isxdigit(lo_nibble_char)) + { + uint8_t hi_nibble = xdigit_to_sint (hi_nibble_char); + uint8_t lo_nibble = xdigit_to_sint (lo_nibble_char); + m_index += 2; + return (hi_nibble << 4) + lo_nibble; + } + } + m_index = UINT32_MAX; + return fail_value; +} + +uint32_t +StringExtractor::GetHexMaxU32 (bool little_endian, uint32_t fail_value) +{ + uint32_t result = 0; + uint32_t nibble_count = 0; + + if (little_endian) + { + uint32_t shift_amount = 0; + while (m_index < m_packet.size() && ::isxdigit (m_packet[m_index])) + { + // Make sure we don't exceed the size of a uint32_t... + if (nibble_count >= (sizeof(uint32_t) * 2)) + { + m_index = UINT32_MAX; + return fail_value; + } + + uint8_t nibble_lo; + uint8_t nibble_hi = xdigit_to_sint (m_packet[m_index]); + ++m_index; + if (m_index < m_packet.size() && ::isxdigit (m_packet[m_index])) + { + nibble_lo = xdigit_to_sint (m_packet[m_index]); + ++m_index; + result |= ((uint32_t)nibble_hi << (shift_amount + 4)); + result |= ((uint32_t)nibble_lo << shift_amount); + nibble_count += 2; + shift_amount += 8; + } + else + { + result |= ((uint32_t)nibble_hi << shift_amount); + nibble_count += 1; + shift_amount += 4; + } + + } + } + else + { + while (m_index < m_packet.size() && ::isxdigit (m_packet[m_index])) + { + // Make sure we don't exceed the size of a uint32_t... + if (nibble_count >= (sizeof(uint32_t) * 2)) + { + m_index = UINT32_MAX; + return fail_value; + } + + uint8_t nibble = xdigit_to_sint (m_packet[m_index]); + // Big Endian + result <<= 4; + result |= nibble; + + ++m_index; + ++nibble_count; + } + } + return result; +} + +uint64_t +StringExtractor::GetHexMaxU64 (bool little_endian, uint64_t fail_value) +{ + uint64_t result = 0; + uint32_t nibble_count = 0; + + if (little_endian) + { + uint32_t shift_amount = 0; + while (m_index < m_packet.size() && ::isxdigit (m_packet[m_index])) + { + // Make sure we don't exceed the size of a uint64_t... + if (nibble_count >= (sizeof(uint64_t) * 2)) + { + m_index = UINT32_MAX; + return fail_value; + } + + uint8_t nibble_lo; + uint8_t nibble_hi = xdigit_to_sint (m_packet[m_index]); + ++m_index; + if (m_index < m_packet.size() && ::isxdigit (m_packet[m_index])) + { + nibble_lo = xdigit_to_sint (m_packet[m_index]); + ++m_index; + result |= ((uint64_t)nibble_hi << (shift_amount + 4)); + result |= ((uint64_t)nibble_lo << shift_amount); + nibble_count += 2; + shift_amount += 8; + } + else + { + result |= ((uint64_t)nibble_hi << shift_amount); + nibble_count += 1; + shift_amount += 4; + } + + } + } + else + { + while (m_index < m_packet.size() && ::isxdigit (m_packet[m_index])) + { + // Make sure we don't exceed the size of a uint64_t... + if (nibble_count >= (sizeof(uint64_t) * 2)) + { + m_index = UINT32_MAX; + return fail_value; + } + + uint8_t nibble = xdigit_to_sint (m_packet[m_index]); + // Big Endian + result <<= 4; + result |= nibble; + + ++m_index; + ++nibble_count; + } + } + return result; +} + +size_t +StringExtractor::GetHexBytes (void *dst_void, size_t dst_len, uint8_t fail_fill_value) +{ + uint8_t *dst = (uint8_t*)dst_void; + size_t bytes_extracted = 0; + while (bytes_extracted < dst_len && GetBytesLeft ()) + { + dst[bytes_extracted] = GetHexU8 (fail_fill_value); + if (IsGood()) + ++bytes_extracted; + else + break; + } + + for (size_t i = bytes_extracted; i < dst_len; ++i) + dst[i] = fail_fill_value; + + return bytes_extracted; +} + + +// Consume ASCII hex nibble character pairs until we have decoded byte_size +// bytes of data. + +uint64_t +StringExtractor::GetHexWithFixedSize (uint32_t byte_size, bool little_endian, uint64_t fail_value) +{ + if (byte_size <= 8 && GetBytesLeft() >= byte_size * 2) + { + uint64_t result = 0; + uint32_t i; + if (little_endian) + { + // Little Endian + uint32_t shift_amount; + for (i = 0, shift_amount = 0; + i < byte_size && m_index != UINT32_MAX; + ++i, shift_amount += 8) + { + result |= ((uint64_t)GetHexU8() << shift_amount); + } + } + else + { + // Big Endian + for (i = 0; i < byte_size && m_index != UINT32_MAX; ++i) + { + result <<= 8; + result |= GetHexU8(); + } + } + } + m_index = UINT32_MAX; + return fail_value; +} + +bool +StringExtractor::GetNameColonValue (std::string &name, std::string &value) +{ + // Read something in the form of NNNN:VVVV; where NNNN is any character + // that is not a colon, followed by a ':' character, then a value (one or + // more ';' chars), followed by a ';' + if (m_index < m_packet.size()) + { + const size_t colon_idx = m_packet.find (':', m_index); + if (colon_idx != std::string::npos) + { + const size_t semicolon_idx = m_packet.find (';', colon_idx); + if (semicolon_idx != std::string::npos) + { + name.assign (m_packet, m_index, colon_idx - m_index); + value.assign (m_packet, colon_idx + 1, semicolon_idx - (colon_idx + 1)); + m_index = semicolon_idx + 1; + return true; + } + } + } + m_index = UINT32_MAX; + return false; +} diff --git a/lldb/source/Utility/StringExtractor.h b/lldb/source/Utility/StringExtractor.h new file mode 100644 index 000000000000..e2312f71ad7a --- /dev/null +++ b/lldb/source/Utility/StringExtractor.h @@ -0,0 +1,126 @@ +//===-- StringExtractor.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_StringExtractor_h_ +#define utility_StringExtractor_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes + +class StringExtractor +{ +public: + + enum { + BigEndian = 0, + LittleEndian = 1 + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StringExtractor(); + StringExtractor(const char *packet_cstr); + StringExtractor(const StringExtractor& rhs); + virtual ~StringExtractor(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const StringExtractor& + operator=(const StringExtractor& rhs); + + // Returns true if the file position is still valid for the data + // contained in this string extractor object. + bool + IsGood() const + { + return m_index != UINT32_MAX; + } + + uint32_t + GetFilePos () const + { + return m_index; + } + + void + SetFilePos (uint32_t idx) + { + m_index = idx; + } + + void + Clear () + { + m_packet.clear(); + m_index = 0; + } + + std::string & + GetStringRef () + { + return m_packet; + } + + bool + Empty() + { + return m_packet.empty(); + } + + uint32_t + GetBytesLeft () + { + if (m_index < m_packet.size()) + return m_packet.size() - m_index; + return 0; + } + char + GetChar (char fail_value = '\0'); + + int8_t + GetHexS8 (int8_t fail_value = 0); + + uint8_t + GetHexU8 (uint8_t fail_value = 0); + + bool + GetNameColonValue (std::string &name, std::string &value); + + uint32_t + GetHexMaxU32 (bool little_endian, uint32_t fail_value); + + uint64_t + GetHexMaxU64 (bool little_endian, uint64_t fail_value); + + size_t + GetHexBytes (void *dst, size_t dst_len, uint8_t fail_fill_value); + + uint64_t + GetHexWithFixedSize (uint32_t byte_size, bool little_endian, uint64_t fail_value); + +protected: + //------------------------------------------------------------------ + // For StringExtractor only + //------------------------------------------------------------------ + std::string m_packet; // The string in which to extract data. + uint32_t m_index; // When extracting data from a packet, this index + // will march along as things get extracted. If set + // to UINT32_MAX the end of the packet data was + // reached when decoding information + + uint32_t + GetNumHexASCIICharsAtFilePos (uint32_t max = UINT32_MAX) const; +}; + +#endif // utility_StringExtractor_h_ diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp new file mode 100644 index 000000000000..f7dcc4181f3f --- /dev/null +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -0,0 +1,89 @@ +//===-- StringExtractorGDBRemote.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StringExtractorGDBRemote.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + + + +StringExtractorGDBRemote::Type +StringExtractorGDBRemote::GetType () const +{ + if (m_packet.empty()) + return eUnsupported; + + switch (m_packet[0]) + { + case 'E': + if (m_packet.size() == 3 && + isxdigit(m_packet[1]) && + isxdigit(m_packet[2])) + return eError; + break; + + case 'O': + if (m_packet.size() == 2 && m_packet[1] == 'K') + return eOK; + break; + + case '+': + if (m_packet.size() == 1) + return eAck; + break; + + case '-': + if (m_packet.size() == 1) + return eNack; + break; + } + return eResponse; +} + +bool +StringExtractorGDBRemote::IsOKPacket() const +{ + return GetType () == eOK; +} + + +bool +StringExtractorGDBRemote::IsUnsupportedPacket() const +{ + return GetType () == eUnsupported; +} + +bool +StringExtractorGDBRemote::IsNormalPacket() const +{ + return GetType () == eResponse; +} + +bool +StringExtractorGDBRemote::IsErrorPacket() const +{ + return GetType () == eError && + m_packet.size() == 3 && + isxdigit(m_packet[1]) && + isxdigit(m_packet[2]); +} + +uint8_t +StringExtractorGDBRemote::GetError () +{ + if (GetType() == eError) + { + SetFilePos(1); + return GetHexU8(255); + } + return 0; +} diff --git a/lldb/source/Utility/StringExtractorGDBRemote.h b/lldb/source/Utility/StringExtractorGDBRemote.h new file mode 100644 index 000000000000..813ddad2e278 --- /dev/null +++ b/lldb/source/Utility/StringExtractorGDBRemote.h @@ -0,0 +1,73 @@ +//===-- StringExtractorGDBRemote.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_StringExtractorGDBRemote_h_ +#define utility_StringExtractorGDBRemote_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "StringExtractor.h" + +class StringExtractorGDBRemote : public StringExtractor +{ +public: + + StringExtractorGDBRemote() : + StringExtractor () + { + } + + StringExtractorGDBRemote(const char *cstr) : + StringExtractor (cstr) + { + } + StringExtractorGDBRemote(const StringExtractorGDBRemote& rhs) : + StringExtractor (rhs) + { + } + + virtual ~StringExtractorGDBRemote() + { + } + + enum Type + { + eUnsupported = 0, + eAck, + eNack, + eError, + eOK, + eResponse + }; + + StringExtractorGDBRemote::Type + GetType () const; + + bool + IsOKPacket() const; + + bool + IsUnsupportedPacket() const; + + bool + IsNormalPacket () const; + + bool + IsErrorPacket() const; + + // Returns zero if the packet isn't a EXX packet where XX are two hex + // digits. Otherwise the error encoded in XX is returned. + uint8_t + GetError(); +}; + +#endif // utility_StringExtractorGDBRemote_h_ diff --git a/lldb/source/lldb-log.cpp b/lldb/source/lldb-log.cpp new file mode 100644 index 000000000000..118a5b306322 --- /dev/null +++ b/lldb/source/lldb-log.cpp @@ -0,0 +1,190 @@ +//===-- lldb-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-private-log.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.h" + + +using namespace lldb; +using namespace lldb_private; + + +static Log * +LogAccessor (bool get, StreamSP *stream_sp_ptr) +{ + static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. + if (!get) + { + if (g_log) + delete g_log; + if (stream_sp_ptr) + g_log = new Log (*stream_sp_ptr); + else + g_log = NULL; + } + + return g_log; + +} + +uint32_t +lldb_private::GetLogMask () +{ + Log *log = LogAccessor (true, NULL); + if (log) + return log->GetMask().GetAllFlagBits(); + return 0; +} + +bool +lldb_private::IsLogVerbose () +{ + uint32_t mask = GetLogMask(); + return (mask & LIBLLDB_LOG_VERBOSE); +} + +Log * +lldb_private::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +lldb_private::LogIfAllCategoriesSet (uint32_t mask, const char *format, ...) +{ + Log *log = GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} + +void +lldb_private::LogIfAnyCategoriesSet (uint32_t mask, const char *format, ...) +{ + Log *log = GetLogIfAnyCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} + +Log * +lldb_private::GetLogIfAnyCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask && (mask & log->GetMask().GetAllFlagBits())) + return log; + return NULL; +} + +void +lldb_private::DisableLog () +{ + LogAccessor (false, NULL); +} + + +Log * +lldb_private::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits; + Log* log = LogAccessor (true, NULL); + if (log) + flag_bits = log->GetMask().GetAllFlagBits(); + else + flag_bits = 0; + + // Now make a new log with this stream. + log = LogAccessor (false, &log_stream_sp); + if (log) + { + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; iPrintf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + return log; + } + } + } + + log->GetMask().SetAllFlagBits(flag_bits); + log->GetOptions().SetAllFlagBits(log_options); + } + return log; +} + + +void +lldb_private::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for 'lldb':\n" + "\tall - turn on all available logging categories\n" + "\tdefault - enable the default set of logging categories for liblldb\n" + "\tbreak - log breakpoints\n" + "\tevents - log broadcaster, listener and event queue activities\n" + "\texpr - log expressions\n" + "\tobject - log object construction/destruction for important objects\n" + "\tprocess - log process events and activities\n" + "\tthread - log thread events and activities\n" + "\tshlib - log shared library related activities\n" + "\tstate - log private and public process state changes\n" + "\tstep - log step related activities\n" + "\tverbose - enable verbose loggging\n" + "\twatch - log watchpoint related activities\n"); +} diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp new file mode 100644 index 000000000000..187f9ea3afbd --- /dev/null +++ b/lldb/source/lldb.cpp @@ -0,0 +1,107 @@ +//===-- lldb.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/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "ABIMacOSX_i386.h" +#include "ABISysV_x86_64.h" +#include "DisassemblerLLVM.h" +#include "DynamicLoaderMacOSXDYLD.h" +#include "ObjectContainerBSDArchive.h" +#include "ObjectContainerUniversalMachO.h" +#include "ObjectFileELF.h" +#include "ObjectFileMachO.h" +#include "ProcessMacOSX.h" +#include "ProcessGDBRemote.h" +#include "SymbolFileDWARF.h" +#include "SymbolFileDWARFDebugMap.h" +#include "SymbolFileSymtab.h" +#include "SymbolVendorMacOSX.h" + +using namespace lldb_private; + + +void +lldb_private::Initialize () +{ + // Make sure we inialize only once + static Mutex g_inited_mutex(Mutex::eMutexTypeNormal); + static bool g_inited = false; + + Mutex::Locker locker(g_inited_mutex); + if (!g_inited) + { + g_inited = true; + Timer::Initialize (); + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + Log::Callbacks log_callbacks = { DisableLog, EnableLog, ListLogCategories }; + + Log::RegisterLogChannel ("lldb", log_callbacks); + ABIMacOSX_i386::Initialize(); + ABISysV_x86_64::Initialize(); + DisassemblerLLVM::Initialize(); + DynamicLoaderMacOSXDYLD::Initialize(); + ObjectContainerUniversalMachO::Initialize(); + ObjectContainerBSDArchive::Initialize(); + ObjectFileELF::Initialize(); + ObjectFileMachO::Initialize(); + ProcessGDBRemote::Initialize(); + ProcessMacOSX::Initialize(); + SymbolFileDWARF::Initialize(); + SymbolFileDWARFDebugMap::Initialize(); + SymbolFileSymtab::Initialize(); + SymbolVendorMacOSX::Initialize(); + } +} + +void +lldb_private::WillTerminate() +{ + Host::WillTerminate(); +} + +void +lldb_private::Terminate () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + DisassemblerLLVM::Terminate(); + DynamicLoaderMacOSXDYLD::Terminate(); + ObjectContainerUniversalMachO::Terminate(); + ObjectContainerBSDArchive::Terminate(); + ObjectFileELF::Terminate(); + ObjectFileMachO::Terminate(); + ProcessGDBRemote::Terminate(); + ProcessMacOSX::Terminate(); + SymbolFileDWARF::Terminate(); + SymbolFileDWARFDebugMap::Terminate(); + SymbolFileSymtab::Terminate(); + SymbolVendorMacOSX::Terminate(); +} + +const char * +lldb_private::GetVersion () +{ + extern const double LLDBVersionNumber; + static char g_version_string[32]; + if (g_version_string[0] == '\0') + ::snprintf (g_version_string, sizeof(g_version_string), "LLDB-%g", LLDBVersionNumber); + + return g_version_string; +} + +ArchSpec & +lldb_private::GetDefaultArchitecture () +{ + static ArchSpec g_default_arch; + return g_default_arch; +} diff --git a/lldb/test/Makefile b/lldb/test/Makefile new file mode 100644 index 000000000000..2221a23319e9 --- /dev/null +++ b/lldb/test/Makefile @@ -0,0 +1,21 @@ +.PHONY: clean all + +#---------------------------------------------------------------------- +# Make all of the test programs +#---------------------------------------------------------------------- +all: + find . -type d -depth 1 | xargs -J % find % \ + -name Makefile \ + -exec echo \; \ + -exec echo make -f '{}' \; \ + -execdir make \; + +#---------------------------------------------------------------------- +# Make all of the test programs +#---------------------------------------------------------------------- +clean: + find . -type d -depth 1 | xargs -J % find % \ + -name Makefile \ + -exec echo \; \ + -exec echo make -f '{}' clean \; \ + -execdir make clean \; diff --git a/lldb/test/array_types/Makefile b/lldb/test/array_types/Makefile new file mode 100644 index 000000000000..7c8745b2ab17 --- /dev/null +++ b/lldb/test/array_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/array_types/cmds.txt b/lldb/test/array_types/cmds.txt new file mode 100644 index 000000000000..8feebe212044 --- /dev/null +++ b/lldb/test/array_types/cmds.txt @@ -0,0 +1,3 @@ +break main.c:42 +continue +var diff --git a/lldb/test/array_types/main.c b/lldb/test/array_types/main.c new file mode 100644 index 000000000000..b6eaf4b0d548 --- /dev/null +++ b/lldb/test/array_types/main.c @@ -0,0 +1,51 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int main (int argc, char const *argv[]) +{ + struct point_tag { + int x; + int y; + }; + + struct rect_tag { + struct point_tag bottom_left; + struct point_tag top_right; + }; + char char_16[16] = "Hello World\n"; + char *strings[] = { "Hello", "Hola", "Bonjour", "Guten Tag" }; + char char_matrix[3][3] = {{'a', 'b', 'c' }, {'d', 'e', 'f' }, {'g', 'h', 'i' }}; + char char_matrix_matrix[3][2][3] = + { {{'a', 'b', 'c' }, {'d', 'e', 'f' }}, + {{'A', 'B', 'C' }, {'D', 'E', 'F' }}, + {{'1', '2', '3' }, {'4', '5', '6' }}}; + short short_4[4] = { 1,2,3,4 }; + short short_matrix[1][2] = { {1,2} }; + unsigned short ushort_4[4] = { 1,2,3,4 }; + short ushort_matrix[2][3] = { + { 1, 2, 3}, + {11,22,33} + }; + int int_2[2] = { 1, 2 }; + unsigned int uint_2[2] = { 1, 2 }; + long long_6[6] = { 1, 2, 3, 4, 5, 6 }; + unsigned long ulong_6[6] = { 1, 2, 3, 4, 5, 6 }; + struct point_tag points_2[2] = { + {1,2}, + {3,4} + }; + struct point_tag points_2_4_matrix[2][4] = { + {{ 1, 2}, { 3, 4}, { 5, 6}, { 7, 8}}, + {{11,22}, {33,44}, {55,66}, {77,88}} + }; + struct rect_tag rects_2[2] = { + {{1,2}, {3,4}}, + {{5,6}, {7,8}} + }; + return 0; +} diff --git a/lldb/test/bitfields/Makefile b/lldb/test/bitfields/Makefile new file mode 100644 index 000000000000..7c8745b2ab17 --- /dev/null +++ b/lldb/test/bitfields/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/bitfields/main.c b/lldb/test/bitfields/main.c new file mode 100644 index 000000000000..5edf5bdd0e05 --- /dev/null +++ b/lldb/test/bitfields/main.c @@ -0,0 +1,44 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include +int main (int argc, char const *argv[]) +{ + struct Bits + { + uint32_t b1 : 1, + b2 : 2, + b3 : 3, + b4 : 4, + b5 : 5, + b6 : 6, + b7 : 7, + four : 4; + }; + + struct Bits bits; + int i; + for (i=0; i<(1<<1); i++) + bits.b1 = i; //// break $source:$line + for (i=0; i<(1<<2); i++) + bits.b2 = i; //// break $source:$line + for (i=0; i<(1<<3); i++) + bits.b3 = i; //// break $source:$line + for (i=0; i<(1<<4); i++) + bits.b4 = i; //// break $source:$line + for (i=0; i<(1<<5); i++) + bits.b5 = i; //// break $source:$line + for (i=0; i<(1<<6); i++) + bits.b6 = i; //// break $source:$line + for (i=0; i<(1<<7); i++) + bits.b7 = i; //// break $source:$line + for (i=0; i<(1<<4); i++) + bits.b4 = i; //// break $source:$line + return 0; //// continue + +} diff --git a/lldb/test/class_types/Makefile b/lldb/test/class_types/Makefile new file mode 100644 index 000000000000..fb5e33230e9d --- /dev/null +++ b/lldb/test/class_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES :=main.cpp +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/class_types/cmds.txt b/lldb/test/class_types/cmds.txt new file mode 100644 index 000000000000..1c7ef9f1c8ad --- /dev/null +++ b/lldb/test/class_types/cmds.txt @@ -0,0 +1,3 @@ +b main.cpp:97 +c +var diff --git a/lldb/test/class_types/main.cpp b/lldb/test/class_types/main.cpp new file mode 100644 index 000000000000..9ca8e935df9d --- /dev/null +++ b/lldb/test/class_types/main.cpp @@ -0,0 +1,106 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +class A +{ +public: + A(int i=0): + m_a_int(i), + m_aa_int(i+1) + { + } + + //virtual + ~A() + { + } + + int + GetInteger() const + { + return m_a_int; + } + void + SetInteger(int i) + { + m_a_int = i; + } + +protected: + int m_a_int; + int m_aa_int; +}; + +class B : public A +{ +public: + B(int ai, int bi) : + A(ai), + m_b_int(bi) + { + } + + //virtual + ~B() + { + } + + int + GetIntegerB() const + { + return m_b_int; + } + void + SetIntegerB(int i) + { + m_b_int = i; + } + +protected: + int m_b_int; +}; + + +class C : public B +{ +public: + C(int ai, int bi, int ci) : + B(ai, bi), + m_c_int(ci) + { + } + + //virtual + ~C() + { + } + + int + GetIntegerC() const + { + return m_c_int; + } + void + SetIntegerC(int i) + { + m_c_int = i; + } + +protected: + int m_c_int; +}; + +int +main (int argc, char const *argv[]) +{ + A a(12); + B b(22,33); + C c(44,55,66); + return b.GetIntegerB() - a.GetInteger() + c.GetInteger(); +} diff --git a/lldb/test/dead-strip/Makefile b/lldb/test/dead-strip/Makefile new file mode 100644 index 000000000000..340b46e36afb --- /dev/null +++ b/lldb/test/dead-strip/Makefile @@ -0,0 +1,126 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) -Xlinker -dead_strip +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +# Don't make the dSYM so we can test DWARF with debug map... +# $(DSYM) : $(EXE) +# $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/dead-strip/cmds.txt b/lldb/test/dead-strip/cmds.txt new file mode 100644 index 000000000000..f6fd04502888 --- /dev/null +++ b/lldb/test/dead-strip/cmds.txt @@ -0,0 +1,4 @@ +b main.c:21 +b main.c:41 +lines -shlib a.out main.c +c diff --git a/lldb/test/dead-strip/main.c b/lldb/test/dead-strip/main.c new file mode 100644 index 000000000000..26dfe25c6940 --- /dev/null +++ b/lldb/test/dead-strip/main.c @@ -0,0 +1,53 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + + +int f1 (char *s); +int f2 (char *s); +int f3 (char *s); + + +// We want f1 to start on line 10 +int f1 (char *s) +{ + return printf("f1: %s\n", s); +} + + + + + +// We want f2 to start on line 20, this should get stripped +int f2 (char *s) +{ + return printf("f2: %s\n", s); +} + + + + + +// We want f3 to start on line 30 +int f3 (char *s) +{ + return printf("f3: %s\n", s); +} + + + + + +// We want main to start on line 40 +int main (int argc, const char * argv[]) +{ + f1("carp"); + f3("dong"); + return 0; +} diff --git a/lldb/test/enum_types/Makefile b/lldb/test/enum_types/Makefile new file mode 100644 index 000000000000..7c8745b2ab17 --- /dev/null +++ b/lldb/test/enum_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/enum_types/main.c b/lldb/test/enum_types/main.c new file mode 100644 index 000000000000..1d837358ddfd --- /dev/null +++ b/lldb/test/enum_types/main.c @@ -0,0 +1,29 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + +int main (int argc, char const *argv[]) +{ + enum days { + Monday = 10, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday, + kNumDays + }; + enum days day; + for (day = Monday - 1; day <= kNumDays + 1; day++) + { + printf("day as int is %i\n", (int)day); + } + return 0; +} diff --git a/lldb/test/function_types/Makefile b/lldb/test/function_types/Makefile new file mode 100644 index 000000000000..7c8745b2ab17 --- /dev/null +++ b/lldb/test/function_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/function_types/main.c b/lldb/test/function_types/main.c new file mode 100644 index 000000000000..3c968e4a0e74 --- /dev/null +++ b/lldb/test/function_types/main.c @@ -0,0 +1,22 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int string_not_empty (const char *s) +{ + if (s && s[0]) + return 1; + return 0; +} + +int main (int argc, char const *argv[]) +{ + int (*callback)(const char *) = string_not_empty; + + return callback(0); +} diff --git a/lldb/test/global_variables/Makefile b/lldb/test/global_variables/Makefile new file mode 100644 index 000000000000..fdf381403b50 --- /dev/null +++ b/lldb/test/global_variables/Makefile @@ -0,0 +1,142 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# dylib settings +#---------------------------------------------------------------------- +DYLIB_NAME=a +DYLIB_BASENAME=lib$(DYLIB_NAME).dylib +DYLIB_C_SOURCES :=a.c +ifneq "$(strip $(DYLIB_C_SOURCES))" "" + DYLIB_OBJECTS +=$(strip $(DYLIB_C_SOURCES:.c=.o)) +endif + + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) $(DYLIB_BASENAME) + $(LD) $(LDFLAGS) $(OBJECTS) -L. -l$(DYLIB_NAME) -o "$(EXE)" + +#---------------------------------------------------------------------- +# Make the dylib +#---------------------------------------------------------------------- +$(DYLIB_BASENAME) : $(DYLIB_OBJECTS) + $(LD) $(LDFLAGS) $(DYLIB_OBJECTS) -install_name "@executable_path/$(DYLIB_BASENAME)" -dynamiclib -o "$(DYLIB_BASENAME)" + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) $(DYLIB_OBJECTS) $(DYLIB_BASENAME) $(DYLIB_BASENAME).dSYM + + + diff --git a/lldb/test/global_variables/a.c b/lldb/test/global_variables/a.c new file mode 100644 index 000000000000..0a9b4f618711 --- /dev/null +++ b/lldb/test/global_variables/a.c @@ -0,0 +1,10 @@ +//===-- a.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int g_a = 123; + diff --git a/lldb/test/global_variables/cmds.txt b/lldb/test/global_variables/cmds.txt new file mode 100644 index 000000000000..6906a0729aeb --- /dev/null +++ b/lldb/test/global_variables/cmds.txt @@ -0,0 +1,3 @@ +break main.c:5 +continue +var -global g_a -global g_global_int diff --git a/lldb/test/global_variables/main.c b/lldb/test/global_variables/main.c new file mode 100644 index 000000000000..13778c29377d --- /dev/null +++ b/lldb/test/global_variables/main.c @@ -0,0 +1,21 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + +int g_file_global_int = 42; +const char *g_file_global_cstr = "g_file_global_cstr"; +static const char *g_file_static_cstr = "g_file_static_cstr"; + +extern int g_a; +int main (int argc, char const *argv[]) +{ + static const char *g_func_static_cstr = "g_func_static_cstr"; + printf ("%s\n", g_file_global_cstr); + return g_file_global_int + g_a; //// break $source:$line; continue; var -global g_a -global g_global_int +} diff --git a/lldb/test/load_unload/Makefile b/lldb/test/load_unload/Makefile new file mode 100644 index 000000000000..54eb2e2ffe82 --- /dev/null +++ b/lldb/test/load_unload/Makefile @@ -0,0 +1,33 @@ +all: a.out liba.dylib libb.dylib libc.dylib + +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 + +a.out: main.o + gcc $(CFLAGS) -o a.out main.o + +main.o: main.c + gcc $(CFLAGS) -c main.c + +liba.dylib: a.o libb.dylib + gcc $(CFLAGS) -dynamiclib -install_name "@executable_path/liba.dylib" -o liba.dylib a.o -L. -lb + dsymutil liba.dylib + +a.o: a.c + gcc $(CFLAGS) -c a.c + +libb.dylib: b.o + gcc $(CFLAGS) -dynamiclib -install_name "@executable_path/libb.dylib" -o libb.dylib b.o + dsymutil libb.dylib + +b.o: b.c + gcc $(CFLAGS) -c b.c + +libc.dylib: c.o + gcc $(CFLAGS) -dynamiclib -install_name "@executable_path/libc.dylib" -o libc.dylib c.o + dsymutil libc.dylib + +c.o: c.c + gcc $(CFLAGS) -c c.c + +clean: + rm -rf *.o *~ *.dylib a.out *.dSYM diff --git a/lldb/test/load_unload/a.c b/lldb/test/load_unload/a.c new file mode 100644 index 000000000000..9d4711772ddd --- /dev/null +++ b/lldb/test/load_unload/a.c @@ -0,0 +1,15 @@ +//===-- a.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +extern int b_function (); + +int +a_function () +{ + return b_function (); +} diff --git a/lldb/test/load_unload/b.c b/lldb/test/load_unload/b.c new file mode 100644 index 000000000000..6c6293236550 --- /dev/null +++ b/lldb/test/load_unload/b.c @@ -0,0 +1,13 @@ +//===-- b.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int +b_function () +{ + return 500; +} diff --git a/lldb/test/load_unload/c.c b/lldb/test/load_unload/c.c new file mode 100644 index 000000000000..b1778b462d0c --- /dev/null +++ b/lldb/test/load_unload/c.c @@ -0,0 +1,13 @@ +//===-- c.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int +c_function () +{ + return 600; +} diff --git a/lldb/test/load_unload/cmds.txt b/lldb/test/load_unload/cmds.txt new file mode 100644 index 000000000000..1e4b198dc0d3 --- /dev/null +++ b/lldb/test/load_unload/cmds.txt @@ -0,0 +1,2 @@ +breakpoint set -n a_function +run diff --git a/lldb/test/load_unload/main.c b/lldb/test/load_unload/main.c new file mode 100644 index 000000000000..9d2882540e3c --- /dev/null +++ b/lldb/test/load_unload/main.c @@ -0,0 +1,72 @@ +//===-- main.c --------------------------------------------------*- 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 + +int +main (int argc, char const *argv[]) +{ + const char *a_name = "@executable_path/liba.dylib"; + const char *c_name = "@executable_path/libc.dylib"; + void *a_dylib_handle = NULL; + void *c_dylib_handle = NULL; + int (*a_function) (void); + + a_dylib_handle = dlopen (a_name, RTLD_NOW); + if (a_dylib_handle == NULL) + { + fprintf (stderr, "%s\n", dlerror()); + exit (1); + } + + a_function = (int (*) ()) dlsym (a_dylib_handle, "a_function"); + if (a_function == NULL) + { + fprintf (stderr, "%s\n", dlerror()); + exit (2); + } + printf ("First time around, got: %d\n", a_function ()); + dlclose (a_dylib_handle); + + c_dylib_handle = dlopen (c_name, RTLD_NOW); + if (c_dylib_handle == NULL) + { + fprintf (stderr, "%s\n", dlerror()); + exit (3); + } + a_function = (int (*) ()) dlsym (c_dylib_handle, "c_function"); + if (a_function == NULL) + { + fprintf (stderr, "%s\n", dlerror()); + exit (4); + } + + a_dylib_handle = dlopen (a_name, RTLD_NOW); + if (a_dylib_handle == NULL) + { + fprintf (stderr, "%s\n", dlerror()); + exit (5); + } + + a_function = (int (*) ()) dlsym (a_dylib_handle, "a_function"); + if (a_function == NULL) + { + fprintf (stderr, "%s\n", dlerror()); + exit (6); + } + printf ("Second time around, got: %d\n", a_function ()); + dlclose (a_dylib_handle); + + return 0; +} diff --git a/lldb/test/namespace/Makefile b/lldb/test/namespace/Makefile new file mode 100644 index 000000000000..fb5e33230e9d --- /dev/null +++ b/lldb/test/namespace/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES :=main.cpp +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/namespace/cmds.txt b/lldb/test/namespace/cmds.txt new file mode 100644 index 000000000000..76bb1bcba759 --- /dev/null +++ b/lldb/test/namespace/cmds.txt @@ -0,0 +1,3 @@ +b main.cpp:54 +c +var diff --git a/lldb/test/namespace/main.cpp b/lldb/test/namespace/main.cpp new file mode 100644 index 000000000000..dda8b93306bc --- /dev/null +++ b/lldb/test/namespace/main.cpp @@ -0,0 +1,69 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace { + typedef unsigned int uint_t; + int i; +} + +namespace A { + typedef unsigned int uint_t; + namespace B { + typedef unsigned int uint_t; + int j; + int myfunc (int a); + int myfunc2(int a) + { + return a + 2; + } + float myfunc (float f) + { + return f - 2.0; + } + } +} + +namespace Y +{ + typedef unsigned int uint_t; + using A::B::j; + int foo; +} + +using A::B::j; // using declaration + +namespace Foo = A::B; // namespace alias + +using Foo::myfunc; // using declaration + +using namespace Foo; // using directive + +namespace A { + namespace B { + using namespace Y; + int k; + } +} + +int Foo::myfunc(int a) +{ + ::uint_t anon_uint = 0; + A::uint_t a_uint = 1; + B::uint_t b_uint = 2; + Y::uint_t y_uint = 3; + i = 3; + j = 4; + return myfunc2(3) + j + i + a + 2 + anon_uint + a_uint + b_uint + y_uint; +} + +int +main (int argc, char const *argv[]) +{ + return Foo::myfunc(12); +} diff --git a/lldb/test/order/Makefile b/lldb/test/order/Makefile new file mode 100644 index 000000000000..c42b4a350566 --- /dev/null +++ b/lldb/test/order/Makefile @@ -0,0 +1,127 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) -Xlinker -order_file ./order-file +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +# Don't make the dSYM so we can test the DWARF with debug map with +# order files +#$(DSYM) : $(EXE) +# $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/order/cmds.txt b/lldb/test/order/cmds.txt new file mode 100644 index 000000000000..8c51dd763bf4 --- /dev/null +++ b/lldb/test/order/cmds.txt @@ -0,0 +1,3 @@ +b main.c:41 +c +lines -shlib a.out main.c diff --git a/lldb/test/order/main.c b/lldb/test/order/main.c new file mode 100644 index 000000000000..1791e7c26587 --- /dev/null +++ b/lldb/test/order/main.c @@ -0,0 +1,54 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + + +int f1 (char *s); +int f2 (char *s); +int f3 (char *s); + + +// We want f1 to start on line 10 +int f1 (char *s) +{ + return printf("f1: %s\n", s); +} + + + + + +// We want f2 to start on line 20 +int f2 (char *s) +{ + return printf("f2: %s\n", s); +} + + + + + +// We want f3 to start on line 30 +int f3 (char *s) +{ + return printf("f3: %s\n", s); +} + + + + + +// We want main to start on line 40 +int main (int argc, const char * argv[]) +{ + f1("carp"); + f2("ding"); + f3("dong"); + return 0; +} diff --git a/lldb/test/order/order-file b/lldb/test/order/order-file new file mode 100644 index 000000000000..0cf8ecd2a639 --- /dev/null +++ b/lldb/test/order/order-file @@ -0,0 +1,4 @@ +main.o:_f3 +main.o:_main +main.o:_f2 +main.o:_f1 diff --git a/lldb/test/print-obj/Makefile b/lldb/test/print-obj/Makefile new file mode 100644 index 000000000000..ee9b07855d1b --- /dev/null +++ b/lldb/test/print-obj/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES := +OBJC_SOURCES := blocked.m +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) -framework Foundation +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/print-obj/blocked.m b/lldb/test/print-obj/blocked.m new file mode 100644 index 000000000000..3f5d44afeadf --- /dev/null +++ b/lldb/test/print-obj/blocked.m @@ -0,0 +1,73 @@ +//===-- blocked.m --------------------------------------------------*- ObjC -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file is for testing running "print object" in a case where another thread +// blocks the print object from making progress. Set a breakpoint on the line in +// my_pthread_routine as indicated. Then switch threads to the main thread, and +// do print the lock_me object. Since that will try to get the lock already gotten +// by my_pthread_routime thread, it will have to switch to running all threads, and +// that should then succeed. +// + +#include +#include + +static pthread_mutex_t test_mutex; + +static void Mutex_Init (void) +{ + pthread_mutexattr_t tmp_mutex_attr; + pthread_mutexattr_init(&tmp_mutex_attr); + pthread_mutex_init(&test_mutex, &tmp_mutex_attr); +} + +@interface LockMe :NSObject +{ + +} +- (NSString *) description; +@end + +@implementation LockMe +- (NSString *) description +{ + printf ("LockMe trying to get the lock.\n"); + pthread_mutex_lock(&test_mutex); + printf ("LockMe got the lock.\n"); + pthread_mutex_unlock(&test_mutex); + return @"I am pretty special.\n"; +} +@end + +void * +my_pthread_routine (void *data) +{ + printf ("my_pthread_routine about to enter.\n"); + pthread_mutex_lock(&test_mutex); + printf ("Releasing Lock.\n"); /// Set a breakpoint here. + pthread_mutex_unlock(&test_mutex); + return NULL; +} + +int +main () +{ + pthread_attr_t tmp_attr; + pthread_attr_init (&tmp_attr); + pthread_t my_pthread; + + Mutex_Init (); + + LockMe *lock_me = [[LockMe alloc] init]; + pthread_create (&my_pthread, &tmp_attr, my_pthread_routine, NULL); + + pthread_join (my_pthread, NULL); + + return 0; +} diff --git a/lldb/test/set_values/Makefile b/lldb/test/set_values/Makefile new file mode 100644 index 000000000000..7c8745b2ab17 --- /dev/null +++ b/lldb/test/set_values/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/set_values/main.c b/lldb/test/set_values/main.c new file mode 100644 index 000000000000..2a8f48d1e0b7 --- /dev/null +++ b/lldb/test/set_values/main.c @@ -0,0 +1,116 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include + +void set_char(void) +{ + char i = 'a'; + printf("before (char) i = %c\n", i); + printf("after (char) i = %c\n", i); //// break $source:$line +} + +void set_uchar(void) +{ + unsigned char i = 'a'; + printf("before (unsigned char) i = %c\n", i); + printf("after (unsigned char) i = %c\n", i); //// break $source:$line +} + +void set_short(void) +{ + short i = 33; + printf("before (short) i = %i\n", i); + printf("after (short) i = %i\n", i); //// break $source:$line +} + +void set_ushort(void) +{ + unsigned short i = 33; + printf("before (unsigned short) i = %i\n", i); + printf("after (unsigned short) i = %i\n", i); //// break $source:$line +} + +void set_int(void) +{ + int i = 33; + printf("before (int) i = %i\n", i); + printf("after (int) i = %i\n", i); //// break $source:$line +} + +void set_uint(void) +{ + unsigned int i = 33; + printf("before (unsigned int) i = %u\n", i); + printf("after (unsigned int) i = %u\n", i); //// break $source:$line +} + +void set_long(void) +{ + long i = 33; + printf("before (long) i = %li\n", i); + printf("after (long) i = %li\n", i); //// break $source:$line +} + +void set_ulong(void) +{ + unsigned long i = 33; + printf("before (unsigned long) i = %lu\n", i); + printf("after (unsigned long) i = %lu\n", i); //// break $source:$line +} + +void set_float(void) +{ + float i = 3.1415927; + printf("before (float) i = %g\n", i); + printf("after (float) i = %g\n", i); //// break $source:$line +} + +void set_double(void) +{ + double i = 3.1415927; + printf("before (double) i = %g\n", i); + printf("after (double) i = %g\n", i); //// break $source:$line +} + +void set_long_double(void) +{ + long double i = 3.1415927; + printf("before (long double) i = %Lg\n", i); + printf("after (long double) i = %Lg\n", i); //// break $source:$line +} + +void set_point (void) +{ + struct point_tag { + int x; + int y; + }; + struct point_tag points_2[2] = { + {1,2}, + {3,4} + }; +} + +int main (int argc, char const *argv[]) +{ + // Continue to the breakpoint in set_char() + set_char(); //// continue; var i; val -set 99 1 + set_uchar(); //// continue; var i; val -set 99 2 + set_short(); //// continue; var i; val -set -42 3 + set_ushort(); //// continue; var i; val -set 42 4 + set_int(); //// continue; var i; val -set -42 5 + set_uint(); //// continue; var i; val -set 42 6 + set_long(); //// continue; var i; val -set -42 7 + set_ulong(); //// continue; var i; val -set 42 8 + set_float(); //// continue; var i; val -set 123.456 9 + set_double(); //// continue; var i; val -set 123.456 10 + set_long_double(); //// continue; var i; val -set 123.456 11 + set_point (); //// continue + return 0; +} diff --git a/lldb/test/signed_types/Makefile b/lldb/test/signed_types/Makefile new file mode 100644 index 000000000000..fb5e33230e9d --- /dev/null +++ b/lldb/test/signed_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES :=main.cpp +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/signed_types/main.cpp b/lldb/test/signed_types/main.cpp new file mode 100644 index 000000000000..fa86e7b49702 --- /dev/null +++ b/lldb/test/signed_types/main.cpp @@ -0,0 +1,31 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int main (int argc, char const *argv[]) +{ + char the_char = 'c'; + short the_short = 'c'; + wchar_t the_whar_t = 'c'; + int the_int = 'c'; + long the_long = 'c'; + long long the_long_long = 'c'; + + signed char the_signed_char = 'c'; + signed short the_signed_short = 'c'; + signed int the_signed_int = 'c'; + signed long the_signed_long = 'c'; + signed long long the_signed_long_long = 'c'; + + return the_char - the_signed_char + + the_short - the_signed_short + + the_int - the_signed_int + + the_long - the_signed_long + + the_long_long - the_signed_long_long; //// break $source:$line; c + //// var the_int + //// val -set 22 1 +} diff --git a/lldb/test/stl/Makefile b/lldb/test/stl/Makefile new file mode 100644 index 000000000000..fa74214a3e1d --- /dev/null +++ b/lldb/test/stl/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES :=main.cpp +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -Os +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/stl/cmds.txt b/lldb/test/stl/cmds.txt new file mode 100644 index 000000000000..9c9c2e3db57b --- /dev/null +++ b/lldb/test/stl/cmds.txt @@ -0,0 +1,3 @@ +b main.cpp:6 +continue +var diff --git a/lldb/test/stl/main.cpp b/lldb/test/stl/main.cpp new file mode 100644 index 000000000000..f294b0e75002 --- /dev/null +++ b/lldb/test/stl/main.cpp @@ -0,0 +1,15 @@ +//===-- main.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 +int main (int argc, char const *argv[]) +{ + std::string hello_world ("Hello World!"); + std::cout << hello_world << std::endl; +} diff --git a/lldb/test/struct_types/Makefile b/lldb/test/struct_types/Makefile new file mode 100644 index 000000000000..7c8745b2ab17 --- /dev/null +++ b/lldb/test/struct_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES :=main.c +CXX_SOURCES := +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/struct_types/cmds.txt b/lldb/test/struct_types/cmds.txt new file mode 100644 index 000000000000..c308a7637fb3 --- /dev/null +++ b/lldb/test/struct_types/cmds.txt @@ -0,0 +1,3 @@ +break main.c:14 +continue +var diff --git a/lldb/test/struct_types/main.c b/lldb/test/struct_types/main.c new file mode 100644 index 000000000000..0d99ee03c6fd --- /dev/null +++ b/lldb/test/struct_types/main.c @@ -0,0 +1,23 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int main (int argc, char const *argv[]) +{ + struct point_tag { + int x; + int y; + }; + + struct rect_tag { + struct point_tag bottom_left; + struct point_tag top_right; + }; + struct point_tag pt = { 2, 3 }; + struct rect_tag rect = {{1,2}, {3,4}}; + return 0; +} diff --git a/lldb/test/tester.py b/lldb/test/tester.py new file mode 100755 index 000000000000..af2c77b0a869 --- /dev/null +++ b/lldb/test/tester.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +import math, os.path, re, sys, time, unittest + +def setupSysPath(): + testPath = sys.path[0] + rem = re.match("(^.*lldb/)test$", testPath) + if not rem: + print "This script expects to reside in .../lldb/test." + sys.exit(-1) + lldbBasePath = rem.group(1) + lldbDebugPythonPath = "build/Debug/LLDB.framework/Resources/Python" + lldbReleasePythonPath = "build/Release/LLDB.framework/Resources/Python" + lldbPythonPath = None + if os.path.isfile(lldbDebugPythonPath + "/lldb.py"): + lldbPythonPath = lldbDebugPythonPath + if os.path.isfile(lldbReleasePythonPath + "/lldb.py"): + lldbPythonPath = lldbReleasePythonPath + if not lldbPythonPath: + print "This script requires lldb.py to be in either " + lldbDebugPythonPath, + print "or" + lldbReleasePythonPath + sys.exit(-1) + sys.path.append(lldbPythonPath) + +def prettyTime(t): + if t == 0.0: + return "0s" + if t < 0.000001: + return ("%.3f" % (t * 1000000000.0)) + "ns" + if t < 0.001: + return ("%.3f" % (t * 1000000.0)) + "µs" + if t < 1: + return ("%.3f" % (t * 1000.0)) + "ms" + return str(t) + "s" + +class ExecutionTimes: + @classmethod + def executionTimes(cls): + if cls.m_executionTimes == None: + cls.m_executionTimes = ExecutionTimes() + for i in range(100): + cls.m_executionTimes.start() + cls.m_executionTimes.end("null") + return cls.m_executionTimes + def __init__(self): + self.m_times = dict() + def start(self): + self.m_start = time.time() + def end(self, component): + e = time.time() + if component not in self.m_times: + self.m_times[component] = list() + self.m_times[component].append(e - self.m_start) + def dumpStats(self): + for key in self.m_times.keys(): + if len(self.m_times[key]): + sampleMin = float('inf') + sampleMax = float('-inf') + sampleSum = 0.0 + sampleCount = 0.0 + for time in self.m_times[key]: + if time > sampleMax: + sampleMax = time + if time < sampleMin: + sampleMin = time + sampleSum += time + sampleCount += 1.0 + sampleMean = sampleSum / sampleCount + sampleVariance = 0 + for time in self.m_times[key]: + sampleVariance += (time - sampleMean) ** 2 + sampleVariance /= sampleCount + sampleStandardDeviation = math.sqrt(sampleVariance) + print key + ": [" + prettyTime(sampleMin) + ", " + prettyTime(sampleMax) + "] ", + print "µ " + prettyTime(sampleMean) + ", σ " + prettyTime(sampleStandardDeviation) + m_executionTimes = None + +setupSysPath() + +import lldb + +class LLDBTestCase(unittest.TestCase): + def setUp(self): + lldb.SBDebugger.SetAsync(True) + self.m_commandInterpreter = lldb.SBDebugger.GetCommandInterpreter() + if not self.m_commandInterpreter: + print "Couldn't get the command interpreter" + sys.exit(-1) + def runCommand(self, command, component): + res = lldb.SBCommandReturnObject() + ExecutionTimes.executionTimes().start() + self.m_commandInterpreter.HandleCommand(command, res, False) + ExecutionTimes.executionTimes().end(component) + if res.Succeeded(): + return res.GetOutput() + else: + self.fail("Command " + command + " returned an error") + return None + +class SanityCheckTestCase(LLDBTestCase): + def runTest(self): + ret = self.runCommand("show arch", "show-arch") + +suite = unittest.TestLoader().loadTestsFromTestCase(SanityCheckTestCase) +unittest.TextTestRunner(verbosity=2).run(suite) +ExecutionTimes.executionTimes().dumpStats() diff --git a/lldb/test/threads/Makefile b/lldb/test/threads/Makefile new file mode 100644 index 000000000000..fb5e33230e9d --- /dev/null +++ b/lldb/test/threads/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES :=main.cpp +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/threads/main.cpp b/lldb/test/threads/main.cpp new file mode 100644 index 000000000000..dddf0087ed28 --- /dev/null +++ b/lldb/test/threads/main.cpp @@ -0,0 +1,129 @@ +//===-- main.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 + +using namespace std; + +pthread_t g_thread_1 = NULL; +pthread_t g_thread_2 = NULL; +pthread_t g_thread_3 = NULL; + +typedef enum { + eGet, + eAssign, + eClearBits +} MaskAction; + +uint32_t mask_access (MaskAction action, uint32_t mask = 0); + +uint32_t +mask_access (MaskAction action, uint32_t mask) +{ + static pthread_mutex_t g_mask_mutex = PTHREAD_MUTEX_INITIALIZER; + static uint32_t g_mask = 0; + ::pthread_mutex_lock (&g_mask_mutex); + switch (action) + { + case eGet: + break; + + case eAssign: + g_mask |= mask; + break; + + case eClearBits: + g_mask &= ~mask; + break; + } + uint32_t new_mask = g_mask; + ::pthread_mutex_unlock (&g_mask_mutex); + return new_mask; +} + +void * +thread_func (void *arg) +{ + uint32_t thread_index = *((uint32_t *)arg); + uint32_t thread_mask = (1u << (thread_index)); + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + while (mask_access(eGet) & thread_mask) + { + // random micro second sleep from zero to 3 seconds + long usec = ::random() % 3000000; + printf ("%s (thread = %u) doing a usleep (%li)...\n", __FUNCTION__, thread_index, usec); + ::usleep (usec); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); + +} + + +int main (int argc, char const *argv[]) +{ + int err; + void *thread_result = NULL; + uint32_t thread_index_1 = 1; + uint32_t thread_index_2 = 2; + uint32_t thread_index_3 = 3; + uint32_t thread_mask_1 = (1u << thread_index_1); + uint32_t thread_mask_2 = (1u << thread_index_2); + uint32_t thread_mask_3 = (1u << thread_index_3); + + // Make a mask that will keep all threads alive + mask_access (eAssign, thread_mask_1 | thread_mask_2 | thread_mask_3); + + // Create 3 threads + err = ::pthread_create (&g_thread_1, NULL, thread_func, &thread_index_1); + err = ::pthread_create (&g_thread_2, NULL, thread_func, &thread_index_2); + err = ::pthread_create (&g_thread_3, NULL, thread_func, &thread_index_3); + + char line[64]; + while (mask_access(eGet) != 0) + { + printf ("Enter thread index to kill or ENTER for all:\n"); + fflush (stdout); + // Kill threads by index, or ENTER for all threads + + if (fgets (line, sizeof(line), stdin)) + { + if (line[0] == '\n' || line[0] == '\r' || line[0] == '\0') + { + printf ("Exiting all threads...\n"); + break; + } + int32_t index = strtoul (line, NULL, 0); + switch (index) + { + case 1: mask_access (eClearBits, thread_mask_1); break; + case 2: mask_access (eClearBits, thread_mask_2); break; + case 3: mask_access (eClearBits, thread_mask_3); break; + } + continue; + } + + break; + } + + // Clear all thread bits to they all exit + mask_access (eClearBits, UINT32_MAX); + + // Join all of our threads + err = ::pthread_join (g_thread_1, &thread_result); + err = ::pthread_join (g_thread_2, &thread_result); + err = ::pthread_join (g_thread_3, &thread_result); + + return 0; +} \ No newline at end of file diff --git a/lldb/test/unsigned_types/Makefile b/lldb/test/unsigned_types/Makefile new file mode 100644 index 000000000000..fb5e33230e9d --- /dev/null +++ b/lldb/test/unsigned_types/Makefile @@ -0,0 +1,125 @@ +#---------------------------------------------------------------------- +# Fill in the source files to build +#---------------------------------------------------------------------- +C_SOURCES := +CXX_SOURCES :=main.cpp +OBJC_SOURCES := +OBJCXX_SOURCES := + +# Uncomment line below for debugging shell commands +# SHELL = /bin/sh -x + +#---------------------------------------------------------------------- +# Change any build/tool options needed +#---------------------------------------------------------------------- +DS := /usr/bin/dsymutil +DSFLAGS = +CFLAGS ?=-arch x86_64 -gdwarf-2 -O0 +CPLUSPLUSFLAGS +=$(CFLAGS) +CPPFLAGS +=$(CFLAGS) +LD = gcc +LDFLAGS = $(CFLAGS) +OBJECTS = +EXE=a.out +DSYM=$(EXE).dSYM + +#---------------------------------------------------------------------- +# Check if we have any C source files +#---------------------------------------------------------------------- +ifneq "$(strip $(C_SOURCES))" "" + OBJECTS +=$(strip $(C_SOURCES:.c=.o)) +endif + +#---------------------------------------------------------------------- +# Check if we have any C++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(CXX_SOURCES))" "" + OBJECTS +=$(strip $(CXX_SOURCES:.cpp=.o)) + LD = g++ +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJC_SOURCES))" "" + OBJECTS +=$(strip $(OBJC_SOURCES:.m=.o)) + LDFLAGS +=-lobjc +endif + +#---------------------------------------------------------------------- +# Check if we have any ObjC++ source files +#---------------------------------------------------------------------- +ifneq "$(strip $(OBJCXX_SOURCES))" "" + OBJECTS +=$(strip $(OBJCXX_SOURCES:.mm=.o)) + LD = g++ + ifeq $(findstring lobjc,$(LDFLAGS)) "" + LDFLAGS +=-lobjc + endif +endif + + +#---------------------------------------------------------------------- +# Make the dSYM file from the executable +#---------------------------------------------------------------------- +$(DSYM) : $(EXE) + $(DS) $(DSFLAGS) -o "$(DSYM)" "$(EXE)" + +#---------------------------------------------------------------------- +# Compile the executable from all the objects (default rule) with no +# dsym file. +#---------------------------------------------------------------------- +$(EXE) : $(OBJECTS) + $(LD) $(LDFLAGS) $(OBJECTS) -o "$(EXE)" + + +#---------------------------------------------------------------------- +# Automatic variables based on items already entered. Below we create +# an objects lists from the list of sources by replacing all entries +# that end with .c with .o, and we also create a list of prerequisite +# files by replacing all .c files with .d. +#---------------------------------------------------------------------- +PREREQS := $(OBJECTS:.o=.d) + +#---------------------------------------------------------------------- +# Rule for Generating Prerequisites Automatically using .d files and +# the compiler -MM option. The -M option will list all system headers, +# and the -MM option will list all non-system dependencies. +#---------------------------------------------------------------------- +%.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.cpp + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.m + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.d: %.mm + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +#---------------------------------------------------------------------- +# Include all of the makefiles for each source file so we don't have +# to manually track all of the prerequisites for each source file. +#---------------------------------------------------------------------- +sinclude $(PREREQS) + +.PHONY: clean +dsym: $(DSYM) +all: $(EXE) $(DSYM) +clean: + rm -rf "$(EXE)" "$(DSYM)" $(OBJECTS) $(PREREQS) + + + diff --git a/lldb/test/unsigned_types/main.cpp b/lldb/test/unsigned_types/main.cpp new file mode 100644 index 000000000000..c18d85a74e71 --- /dev/null +++ b/lldb/test/unsigned_types/main.cpp @@ -0,0 +1,22 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +int main (int argc, char const *argv[]) +{ + typedef unsigned int uint32_t; + unsigned char the_unsigned_char = 'c'; + unsigned short the_unsigned_short = 'c'; + unsigned int the_unsigned_int = 'c'; + unsigned long the_unsigned_long = 'c'; + unsigned long long the_unsigned_long_long = 'c'; + uint32_t the_uint32 = 'c'; + + return the_unsigned_char - the_unsigned_short + + the_unsigned_int - the_unsigned_long + + the_unsigned_long_long - the_uint32; +} diff --git a/lldb/tools/debugserver/debugnub-exports b/lldb/tools/debugserver/debugnub-exports new file mode 100644 index 000000000000..662bf9308a6f --- /dev/null +++ b/lldb/tools/debugserver/debugnub-exports @@ -0,0 +1,2 @@ +_DNB* +__DNB* diff --git a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..14f84acc9f9c --- /dev/null +++ b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -0,0 +1,604 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9CC1192280900958FBD /* StringExtractor.cpp */; }; + 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; }; + 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; }; + 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; }; + 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; }; + 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; }; + 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; }; + 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; }; + 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + 26CE05B0115C36340022F371 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; }; + 26CE05B1115C36350022F371 /* MachProcess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.cpp */; }; + 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; }; + 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; }; + 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; }; + 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; }; + 26CE05B6115C36390022F371 /* MachTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.cpp */; }; + 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; }; + 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; }; + 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; }; + 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; }; + 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; }; + 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; }; + 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; }; + 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; }; + 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; }; + 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; }; + 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; }; + 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; }; + 26CE05C3115C36580022F371 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; }; + 26CE05C4115C36590022F371 /* CFData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */; }; + 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; }; + 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; }; + 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBRuntimeAction.h; sourceTree = ""; }; + 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBThreadResumeActions.cpp; sourceTree = ""; }; + 260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBThreadResumeActions.h; sourceTree = ""; }; + 260FC7320E5B290400043FC9 /* debugnub-exports */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "debugnub-exports"; sourceTree = SOURCE_ROOT; }; + 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "debugserver-entitlements.plist"; sourceTree = ""; }; + 26593A060D4931CC001C9FE3 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + 2660D9CC1192280900958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = ../../source/Utility/StringExtractor.cpp; sourceTree = SOURCE_ROOT; }; + 2660D9CD1192280900958FBD /* StringExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = ../../source/Utility/StringExtractor.h; sourceTree = SOURCE_ROOT; }; + 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadMutex.cpp; sourceTree = ""; }; + 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = DNBArchImpl.cpp; path = arm/DNBArchImpl.cpp; sourceTree = ""; }; + 2675D4230CCEB705000F49AF /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = DNBArchImpl.h; path = arm/DNBArchImpl.h; sourceTree = ""; }; + 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFBundle.cpp; sourceTree = ""; }; + 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFBundle.h; sourceTree = ""; }; + 2695DD9A0D3EC160007E4CA2 /* CFString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFString.h; sourceTree = ""; }; + 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFString.cpp; sourceTree = ""; }; + 2695DE2D0D3EE55B007E4CA2 /* CFData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFData.h; sourceTree = ""; }; + 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFData.cpp; sourceTree = ""; }; + 269DE5C50CB5B723008989F0 /* ProfileObjectiveC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileObjectiveC.h; sourceTree = ""; }; + 269DE5C60CB5B723008989F0 /* ProfileObjectiveC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProfileObjectiveC.cpp; sourceTree = ""; }; + 26A02918114AB9240029C479 /* debugserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugserver.cpp; sourceTree = ""; }; + 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.plist; sourceTree = ""; }; + 26A68F7D0D104EC800665A9E /* RNBContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBContext.h; sourceTree = ""; }; + 26A68F7E0D104EC800665A9E /* RNBContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBContext.cpp; sourceTree = ""; }; + 26A68FAF0D1054DA00665A9E /* RNBSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBSocket.h; sourceTree = ""; }; + 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBSocket.cpp; sourceTree = ""; }; + 26A68FD50D10574500665A9E /* RNBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBRemote.h; sourceTree = ""; }; + 26A68FD60D10574500665A9E /* RNBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBRemote.cpp; sourceTree = ""; }; + 26A8FE1E0D11A77B00203048 /* DNBTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBTimer.h; sourceTree = ""; }; + 26A901EA0EA3F46B00F7C71E /* FunctionProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FunctionProfiler.cpp; sourceTree = ""; }; + 26A901EB0EA3F46B00F7C71E /* FunctionProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FunctionProfiler.h; sourceTree = ""; }; + 26ACA3340D3E956300A2120B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 26B67DE00EE9BC30006C8BC0 /* MachTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachTask.h; sourceTree = ""; }; + 26B67DE10EE9BC30006C8BC0 /* MachTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachTask.cpp; sourceTree = ""; }; + 26C636AD0C71303A0024798E /* dbgnub-config.pl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.perl; path = "dbgnub-config.pl"; sourceTree = ""; }; + 26C637D60C71334A0024798E /* DNB.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNB.cpp; sourceTree = ""; }; + 26C637D70C71334A0024798E /* DNB.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNB.h; sourceTree = ""; }; + 26C637D80C71334A0024798E /* DNBArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArch.h; sourceTree = ""; }; + 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBBreakpoint.cpp; sourceTree = ""; }; + 26C637DA0C71334A0024798E /* DNBBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBBreakpoint.h; sourceTree = ""; }; + 26C637DB0C71334A0024798E /* DNBDataRef.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBDataRef.cpp; sourceTree = ""; }; + 26C637DC0C71334A0024798E /* DNBDataRef.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBDataRef.h; sourceTree = ""; }; + 26C637DD0C71334A0024798E /* DNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBDefs.h; sourceTree = ""; }; + 26C637DE0C71334A0024798E /* DNBError.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBError.cpp; sourceTree = ""; }; + 26C637DF0C71334A0024798E /* DNBError.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBError.h; sourceTree = ""; }; + 26C637E00C71334A0024798E /* DNBLog.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBLog.cpp; sourceTree = ""; }; + 26C637E10C71334A0024798E /* DNBLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBLog.h; sourceTree = ""; }; + 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBRegisterInfo.cpp; sourceTree = ""; }; + 26C637E30C71334A0024798E /* DNBRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBRegisterInfo.h; sourceTree = ""; }; + 26C637E70C71334A0024798E /* CFUtils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CFUtils.h; sourceTree = ""; }; + 26C637E80C71334A0024798E /* dbgnub-mig.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 30; path = "dbgnub-mig.defs"; sourceTree = ""; }; + 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplI386.cpp; sourceTree = ""; }; + 26C637EB0C71334A0024798E /* DNBArchImplI386.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImplI386.h; sourceTree = ""; }; + 26C637EC0C71334A0024798E /* MachDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachDYLD.cpp; sourceTree = ""; }; + 26C637ED0C71334A0024798E /* MachDYLD.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachDYLD.h; sourceTree = ""; }; + 26C637EE0C71334A0024798E /* MachException.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachException.cpp; sourceTree = ""; }; + 26C637EF0C71334A0024798E /* MachException.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachException.h; sourceTree = ""; }; + 26C637F00C71334A0024798E /* MachProcess.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachProcess.cpp; sourceTree = ""; }; + 26C637F10C71334A0024798E /* MachProcess.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachProcess.h; sourceTree = ""; }; + 26C637F20C71334A0024798E /* MachThread.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachThread.cpp; sourceTree = ""; }; + 26C637F30C71334A0024798E /* MachThread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachThread.h; sourceTree = ""; }; + 26C637F40C71334A0024798E /* MachThreadList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachThreadList.cpp; sourceTree = ""; }; + 26C637F50C71334A0024798E /* MachThreadList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachThreadList.h; sourceTree = ""; }; + 26C637F60C71334A0024798E /* MachVMMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMMemory.cpp; sourceTree = ""; }; + 26C637F70C71334A0024798E /* MachVMMemory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachVMMemory.h; sourceTree = ""; }; + 26C637F80C71334A0024798E /* MachVMRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMRegion.cpp; sourceTree = ""; }; + 26C637F90C71334A0024798E /* MachVMRegion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachVMRegion.h; sourceTree = ""; }; + 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImpl.cpp; sourceTree = ""; }; + 26C637FC0C71334A0024798E /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImpl.h; sourceTree = ""; }; + 26C637FD0C71334A0024798E /* PThreadCondition.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadCondition.h; sourceTree = ""; }; + 26C637FE0C71334A0024798E /* PThreadEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadEvent.cpp; sourceTree = ""; }; + 26C637FF0C71334A0024798E /* PThreadEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadEvent.h; sourceTree = ""; }; + 26C638000C71334A0024798E /* PThreadMutex.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadMutex.h; sourceTree = ""; }; + 26C638010C71334A0024798E /* SysSignal.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SysSignal.cpp; sourceTree = ""; }; + 26C638020C71334A0024798E /* SysSignal.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SysSignal.h; sourceTree = ""; }; + 26C638050C71334A0024798E /* TTYState.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = TTYState.cpp; sourceTree = ""; }; + 26C638060C71334A0024798E /* TTYState.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TTYState.h; sourceTree = ""; }; + 26CE0594115C31C20022F371 /* debugserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = debugserver; sourceTree = BUILT_PRODUCTS_DIR; }; + 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplX86_64.cpp; sourceTree = ""; }; + 26CF99A31142EB7400011AAB /* DNBArchImplX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBArchImplX86_64.h; sourceTree = ""; }; + 26E6B9DA0D1329010037ECDD /* RNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBDefs.h; sourceTree = ""; }; + AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoTerminal.cpp; sourceTree = ""; }; + AF67AC000D34604D0022D128 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoTerminal.h; sourceTree = ""; }; + EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.plist; sourceTree = ""; }; + EF88789F0D9C797C001831DA /* RNBServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBServices.h; sourceTree = ""; }; + EF8878A00D9C797C001831DA /* RNBServices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBServices.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 26CE0592115C31C20022F371 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* dbgnub */ = { + isa = PBXGroup; + children = ( + 26ACA3330D3E94F200A2120B /* Framework */, + 26C637D50C71334A0024798E /* source */, + 26C636AC0C71303A0024798E /* scripts */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = dbgnub; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 26CE0594115C31C20022F371 /* debugserver */, + ); + name = Products; + sourceTree = ""; + }; + 2675D41C0CCEB6CF000F49AF /* arm */ = { + isa = PBXGroup; + children = ( + 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */, + 2675D4230CCEB705000F49AF /* DNBArchImpl.h */, + ); + name = arm; + sourceTree = ""; + }; + 26A028FE114AB6A60029C479 /* Resources */ = { + isa = PBXGroup; + children = ( + 260FC7320E5B290400043FC9 /* debugnub-exports */, + 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */, + 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */, + EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 26A028FF114AB6BB0029C479 /* libdebugnub */ = { + isa = PBXGroup; + children = ( + 26C637E60C71334A0024798E /* MacOSX */, + 260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */, + 26A8FE1E0D11A77B00203048 /* DNBTimer.h */, + 26C637D70C71334A0024798E /* DNB.h */, + 26C637D60C71334A0024798E /* DNB.cpp */, + 26C637D80C71334A0024798E /* DNBArch.h */, + 26C637DA0C71334A0024798E /* DNBBreakpoint.h */, + 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */, + 26C637DC0C71334A0024798E /* DNBDataRef.h */, + 26C637DB0C71334A0024798E /* DNBDataRef.cpp */, + 26C637DD0C71334A0024798E /* DNBDefs.h */, + 26C637DF0C71334A0024798E /* DNBError.h */, + 26C637DE0C71334A0024798E /* DNBError.cpp */, + 26C637E10C71334A0024798E /* DNBLog.h */, + 26C637E00C71334A0024798E /* DNBLog.cpp */, + 26C637E30C71334A0024798E /* DNBRegisterInfo.h */, + 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */, + 260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */, + 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */, + AF67AC000D34604D0022D128 /* PseudoTerminal.h */, + AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */, + 26C637FD0C71334A0024798E /* PThreadCondition.h */, + 26C637FF0C71334A0024798E /* PThreadEvent.h */, + 26C637FE0C71334A0024798E /* PThreadEvent.cpp */, + 26C638000C71334A0024798E /* PThreadMutex.h */, + 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */, + 26C638020C71334A0024798E /* SysSignal.h */, + 26C638010C71334A0024798E /* SysSignal.cpp */, + 26C638060C71334A0024798E /* TTYState.h */, + 26C638050C71334A0024798E /* TTYState.cpp */, + ); + name = libdebugnub; + sourceTree = ""; + }; + 26A02900114AB6DB0029C479 /* Utility */ = { + isa = PBXGroup; + children = ( + 26A901EB0EA3F46B00F7C71E /* FunctionProfiler.h */, + 26A901EA0EA3F46B00F7C71E /* FunctionProfiler.cpp */, + 269DE5C50CB5B723008989F0 /* ProfileObjectiveC.h */, + 269DE5C60CB5B723008989F0 /* ProfileObjectiveC.cpp */, + ); + name = Utility; + sourceTree = ""; + }; + 26ACA3330D3E94F200A2120B /* Framework */ = { + isa = PBXGroup; + children = ( + 26ACA3340D3E956300A2120B /* CoreFoundation.framework */, + ); + name = Framework; + sourceTree = ""; + }; + 26C636AC0C71303A0024798E /* scripts */ = { + isa = PBXGroup; + children = ( + 26C636AD0C71303A0024798E /* dbgnub-config.pl */, + ); + path = scripts; + sourceTree = ""; + }; + 26C637D50C71334A0024798E /* source */ = { + isa = PBXGroup; + children = ( + 26593A060D4931CC001C9FE3 /* ChangeLog */, + 26DEFD6C0D104C23008A5A07 /* debugserver */, + 26A028FF114AB6BB0029C479 /* libdebugnub */, + 26A02900114AB6DB0029C479 /* Utility */, + ); + indentWidth = 4; + path = source; + sourceTree = ""; + tabWidth = 4; + usesTabs = 0; + }; + 26C637E60C71334A0024798E /* MacOSX */ = { + isa = PBXGroup; + children = ( + 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */, + 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */, + 2695DE2D0D3EE55B007E4CA2 /* CFData.h */, + 2695DE2E0D3EE55B007E4CA2 /* CFData.cpp */, + 2695DD9A0D3EC160007E4CA2 /* CFString.h */, + 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */, + 26C637E70C71334A0024798E /* CFUtils.h */, + 2675D41C0CCEB6CF000F49AF /* arm */, + 26C637E90C71334A0024798E /* i386 */, + 26C637FA0C71334A0024798E /* ppc */, + 26CF99A11142EB7400011AAB /* x86_64 */, + 26C637E80C71334A0024798E /* dbgnub-mig.defs */, + 26C637ED0C71334A0024798E /* MachDYLD.h */, + 26C637EC0C71334A0024798E /* MachDYLD.cpp */, + 26C637EF0C71334A0024798E /* MachException.h */, + 26C637EE0C71334A0024798E /* MachException.cpp */, + 26C637F10C71334A0024798E /* MachProcess.h */, + 26C637F00C71334A0024798E /* MachProcess.cpp */, + 26C637F30C71334A0024798E /* MachThread.h */, + 26C637F20C71334A0024798E /* MachThread.cpp */, + 26C637F50C71334A0024798E /* MachThreadList.h */, + 26C637F40C71334A0024798E /* MachThreadList.cpp */, + 26C637F70C71334A0024798E /* MachVMMemory.h */, + 26C637F60C71334A0024798E /* MachVMMemory.cpp */, + 26C637F90C71334A0024798E /* MachVMRegion.h */, + 26C637F80C71334A0024798E /* MachVMRegion.cpp */, + 26B67DE00EE9BC30006C8BC0 /* MachTask.h */, + 26B67DE10EE9BC30006C8BC0 /* MachTask.cpp */, + ); + path = MacOSX; + sourceTree = ""; + }; + 26C637E90C71334A0024798E /* i386 */ = { + isa = PBXGroup; + children = ( + 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */, + 26C637EB0C71334A0024798E /* DNBArchImplI386.h */, + ); + path = i386; + sourceTree = ""; + }; + 26C637FA0C71334A0024798E /* ppc */ = { + isa = PBXGroup; + children = ( + 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */, + 26C637FC0C71334A0024798E /* DNBArchImpl.h */, + ); + path = ppc; + sourceTree = ""; + }; + 26CF99A11142EB7400011AAB /* x86_64 */ = { + isa = PBXGroup; + children = ( + 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */, + 26CF99A31142EB7400011AAB /* DNBArchImplX86_64.h */, + ); + path = x86_64; + sourceTree = ""; + }; + 26DEFD6C0D104C23008A5A07 /* debugserver */ = { + isa = PBXGroup; + children = ( + 26A02918114AB9240029C479 /* debugserver.cpp */, + 26A028FE114AB6A60029C479 /* Resources */, + 26A68F7D0D104EC800665A9E /* RNBContext.h */, + 26A68F7E0D104EC800665A9E /* RNBContext.cpp */, + EF88789F0D9C797C001831DA /* RNBServices.h */, + EF8878A00D9C797C001831DA /* RNBServices.cpp */, + 26A68FAF0D1054DA00665A9E /* RNBSocket.h */, + 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */, + 26A68FD50D10574500665A9E /* RNBRemote.h */, + 26A68FD60D10574500665A9E /* RNBRemote.cpp */, + 26E6B9DA0D1329010037ECDD /* RNBDefs.h */, + 2660D9CD1192280900958FBD /* StringExtractor.h */, + 2660D9CC1192280900958FBD /* StringExtractor.cpp */, + ); + name = debugserver; + sourceTree = ""; + usesTabs = 0; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 26CE0593115C31C20022F371 /* debugserver */ = { + isa = PBXNativeTarget; + buildConfigurationList = 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */; + buildPhases = ( + 26CE05C7115C36870022F371 /* ShellScript */, + 26CE0591115C31C20022F371 /* Sources */, + 26CE0592115C31C20022F371 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = debugserver; + productName = "lldb-debugserver"; + productReference = 26CE0594115C31C20022F371 /* debugserver */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* dbgnub */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 26CE0593115C31C20022F371 /* debugserver */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 26CE05C7115C36870022F371 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/scripts/dbgnub-config.pl", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/DNBConfig.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "perl -x scripts/dbgnub-config.pl\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 26CE0591115C31C20022F371 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */, + 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */, + 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */, + 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */, + 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */, + 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */, + 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */, + 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */, + 26CE05B0115C36340022F371 /* MachException.cpp in Sources */, + 26CE05B1115C36350022F371 /* MachProcess.cpp in Sources */, + 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */, + 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */, + 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */, + 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */, + 26CE05B6115C36390022F371 /* MachTask.cpp in Sources */, + 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */, + 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */, + 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */, + 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */, + 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */, + 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */, + 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */, + 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */, + 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */, + 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */, + 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */, + 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */, + 26CE05C3115C36580022F371 /* CFString.cpp in Sources */, + 26CE05C4115C36590022F371 /* CFData.cpp in Sources */, + 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */, + 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */, + 2660D9CE1192280900958FBD /* StringExtractor.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB914F08733D8E0010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 112; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + STRIP_INSTALLED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = Debug; + }; + 1DEB915008733D8E0010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CURRENT_PROJECT_VERSION = 112; + DEAD_CODE_STRIPPING = YES; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + STRIPFLAGS = "-x"; + STRIP_STYLE = debugging; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = Release; + }; + 262419A11198A93E00067686 /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CURRENT_PROJECT_VERSION = 112; + DEAD_CODE_STRIPPING = YES; + FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + STRIPFLAGS = "-x"; + STRIP_STYLE = debugging; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_BUILDER = "$(USER)"; + }; + name = BuildAndIntegration; + }; + 262419A21198A93E00067686 /* BuildAndIntegration */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + x86_64, + i386, + ); + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 112; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER; + INSTALL_PATH = /Developer/usr/bin; + LLDB_DEBUGSERVER = 1; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PREBINDING = NO; + PRODUCT_NAME = debugserver; + STRIP_INSTALLED_PRODUCT = YES; + USER_HEADER_SEARCH_PATHS = "./source $(DERIVED_SOURCES_DIR)"; + ZERO_LINK = NO; + }; + name = BuildAndIntegration; + }; + 26CE0596115C31C30022F371 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + x86_64, + i386, + ); + CODE_SIGN_IDENTITY = lldb_codesign; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 112; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER; + INSTALL_PATH = /Developer/usr/bin; + LLDB_DEBUGSERVER = 1; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PREBINDING = NO; + PRODUCT_NAME = debugserver; + USER_HEADER_SEARCH_PATHS = "./source $(DERIVED_SOURCES_DIR)"; + ZERO_LINK = NO; + }; + name = Debug; + }; + 26CE0597115C31C30022F371 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + x86_64, + i386, + ); + CODE_SIGN_IDENTITY = lldb_codesign; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 112; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER; + INSTALL_PATH = /Developer/usr/bin; + LLDB_DEBUGSERVER = 1; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist", + ); + OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)"; + PREBINDING = NO; + PRODUCT_NAME = debugserver; + USER_HEADER_SEARCH_PATHS = "./source $(DERIVED_SOURCES_DIR)"; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB914F08733D8E0010E9CD /* Debug */, + 1DEB915008733D8E0010E9CD /* Release */, + 262419A11198A93E00067686 /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; + 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 26CE0596115C31C30022F371 /* Debug */, + 26CE0597115C31C30022F371 /* Release */, + 262419A21198A93E00067686 /* BuildAndIntegration */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = BuildAndIntegration; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist b/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist new file mode 100644 index 000000000000..ae9b6b28b112 --- /dev/null +++ b/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${EXECUTABLE_NAME} + CFBundleVersion + 2 + SecTaskAccess + + allowed + debug + + + diff --git a/lldb/tools/debugserver/resources/lldb-debugserver-entitlements.plist b/lldb/tools/debugserver/resources/lldb-debugserver-entitlements.plist new file mode 100644 index 000000000000..42cfb9cdc0e7 --- /dev/null +++ b/lldb/tools/debugserver/resources/lldb-debugserver-entitlements.plist @@ -0,0 +1,8 @@ + + + + + get-task-allow + + + diff --git a/lldb/tools/debugserver/scripts/dbgnub-config.pl b/lldb/tools/debugserver/scripts/dbgnub-config.pl new file mode 100644 index 000000000000..c953cc2e1c5e --- /dev/null +++ b/lldb/tools/debugserver/scripts/dbgnub-config.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +use strict; +my $config_file = "$ENV{SCRIPT_OUTPUT_FILE_0}"; + +# Define the tests we need to run during this configuration +my @config_tests = ( + { + NAME => "HAVE_64_BIT_MACH_EXCEPTIONS", + TEST => "-e '$ENV{SDKROOT}/usr/include/mach/mach_exc.defs'", + COMMENT => "// Defined if we can use 64 bit mach exceptions", + FAIL => "#undef HAVE_64_BIT_MACH_EXCEPTIONS\ +#define mach_exception_data_t exception_data_t\ +#define mach_exception_data_type_t exception_data_type_t\ +#define mach_exc_server exc_server\ +#define MACH_EXCEPTION_CODES 0\n", + SUCCESS => "#define HAVE_64_BIT_MACH_EXCEPTIONS 1\n", + } +); + +#---------------------------------------------------------------------- +# Open the config file +#---------------------------------------------------------------------- +open(CONFIG, "> $config_file") || die "Couldn't open '$config_file' for writing: $!\n"; +print CONFIG "/*" . "-" x 72 . "\n"; +print CONFIG "// This file is auto generated by a dbgnub-config.pl, do not edit by hand!\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// COMMAND LINE\n"; +print CONFIG "// " . join(' ', @ARGV) . "\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// ENVIRONMENT\n"; +my $key; +my $val; +while (($key, $val) = each %ENV) +{ + printf CONFIG "// %s = %s\n", $key, $val; +} +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// SETTINGS\n"; +print CONFIG "// config_file: '$config_file'\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "*/\n\n"; +print CONFIG "#ifndef __DBGNUB_CONFIG__\n"; +print CONFIG "#define __DBGNUB_CONFIG__\n"; + + +#---------------------------------------------------------------------- +# Run the tests +#---------------------------------------------------------------------- +foreach my $test_href (@config_tests) +{ + if (exists $test_href->{COMMENT}) { + print CONFIG "\n$test_href->{COMMENT}\n"; + } else { + print CONFIG "\n// $test_href->{NAME}\n"; + } + + my $test_result = eval "$test_href->{TEST}"; + if ($test_result != 0) + { + print CONFIG "$test_href->{SUCCESS}\n"; + } + else + { + print CONFIG "$test_href->{FAIL}\n"; + } +} + +print CONFIG "#endif // #ifndef __DBGNUB_CONFIG__\n"; +close(CONFIG); + diff --git a/lldb/tools/debugserver/source/ChangeLog b/lldb/tools/debugserver/source/ChangeLog new file mode 100644 index 000000000000..2f3843bbc60f --- /dev/null +++ b/lldb/tools/debugserver/source/ChangeLog @@ -0,0 +1,1515 @@ +2010-01-29 Greg Clayton + + * MachProcess.cpp (MachProcess::PrepareForAttach): No longer use the + SBSLaunchApplication macro from the SpringBoard.framework, use the actual + function name SBSLaunchApplicationForDebugging. + (MachProcess::CleanupAfterAttach): Ditto. + (MachProcess::SBForkChildForPTraceDebugging): Ditto. + (debugserver-entitlements.plist): Added the "seatbelt-profiles" entitlement + so debugserver can be sandboxed. + +2009-07-06 Greg Clayton + + * MachTask.cpp (MachTask::GetDYLDAllImageInfosAddress): Hack around bad + kernel code that renamed the first member of the TASK_DYLD_INFO without + any way to detect it has changed. + +2009-06-29 Greg Clayton + + * DNB.cpp (GetAllInfosMatchingName): Correctly truncate process name string + to MAXCOMLEN when searching kinfo_proc structs for process matches by name. + * MachProcess.cpp (MachProcess::PrepareForAttach): Added logging when + attaching to a program by name. + +2009-06-25 Greg Clayton + + * DNB.cpp (DNBProcessLaunch): Added a stat on the incoming path that we are + about to launch to make sure the file exists. If the file doesn't, then an + appropriate error string is returned. Also if we fail to get the task for + our process ID, we return an error string right away instead of letting the + debug session go for a little bit and then later failing after a few more + packets. + +2009-04-07 Jim Ingham + + * RNBRemote.h: Add vAttachWait + * RNBRemote.cpp (RNBRemote::CreatePacketTable): Add vattachwait. + (RNBRemoteShouldCancelCallback): New function. + (RNBRemote::HandlePacket_v): Handle vattachwait. + * RNBSocket.cpp (RNBSocket::Read): Mark the connection as closed when the + port goes away. + * DNB.cpp (DNBProcessAttachByName): New function. + (DNBProcessAttach): Make this handle catching the attach when done and + dealing with timeout & return conditions. + (GetAllInfos): New function. + (GetAlInfosMatchingName): New function. + (DNBProcessAttachWait): New function. + DNB.h: Declare DNBProcessAttachByName, DNBProcessAttachWait, change + signature of DNBProcessAttach. + * MachProcess.cpp (MachProcess::PrepareForAttach): New function. + (MachProcess::CheckForProcess): New function. + (MachProcess::CleanupAfterAttach): New function. + (CopyBundleIDForPath): New function. + (MachProcess::SBForkChildForPTraceDebugging): Convert to using + CopyBundleIDForPath. + * MachProcess.h: Declare PrepareForAttach, CleanupAfterAttach and + CheckForProcess. + * DNBTimer.h (TimeOfDayLaterThan): New function. + * test-remotenub.cpp (RNBRunLoopGetStartModeFromRemote): Rename from + RNBRunLoopGetArgsFromRemote, and handle vattachwait. + (RNBRunLoopLaunchAttaching): Code was moved from here into DNBProcessAttach. + (StartListening): New function. + (GetAllProcessInfos, GetAllProcessInfosMatchingName): Moved to + DNBProcess.cpp. + (main): Handle attach waitfor, and make debugserver with only a host and + port wait on commands from gdb. + +2009-04-03 Greg Clayton + + * RNBRemote.h (PacketEnum): Added enum for qShlibInfoAddr. + * RNBRemote.cpp (RNBRemote::CreatePacketTable) Added the qShlibInfoAddr + packet definition to m_packets. + (RNBRemote::GetPacket): Log when we run into an unimplemented packet. + (RNBRemote::HandleReceivedPacket): Only log the packet when logging + LOG_RNB_REMOTE. + (RNBRemote::HandlePacket_q): Add support for the new qShlibInfoAddr packet. + * DNB.h (DNBProcessGetSharedLibraryInfoAddress): New prototype. + * DNB.cpp (DNBProcessGetSharedLibraryInfoAddress): New function. + * MachTask.h (MachProcess::GetDYLDAllImageInfosAddress): New prototype. + * MachTask.cpp (MachProcess::GetDYLDAllImageInfosAddress): New function. + +2009-04-01 Greg Clayton + + * test-remotenub.cpp (main): Display the detailed error message if any when + attaching fails. + +2009-03-25 Greg Clayton + + * test-remotenub.cpp (RNBRunLoopGetArgsFromRemote): Cleaned up logging and + removed time deltas form the messages. + (RNBRunLoopLaunchAttaching): Ditto. + (RNBRunLoopLaunchInferior): Ditto and also use new DNBProcessLaunch that + takes an error string pointer. + * RNBContext.h (class RNBContext): Removed the m_timer member. + * RNBContext.cpp (RNBContext::StartProcessStatusThread): Cleaned up logging + and removed time deltas form the messages. + (RNBContext::ThreadFunctionProcessStatus): Ditto. + * RNBSocket.h (class RNBSocket): Removed unused m_last_errno member and + accessor functions. + * RNBSocket.cpp (RNBSocket::Listen): Cleaned up logging and + removed time deltas form the messages. + (RNBSocket::ConnectToService): Ditto. + (RNBSocket::Read): Ditto. + (RNBSocket::Write): Ditto. + (RNBSocket::SaveErrno): Removed. + (RNBSocket::ClosePort): Don't call RNBSocket::SaveErrno(). + * RNBRemote.cpp (RNBRemote::RNBRemote): Cleaned up logging and + removed time deltas form the messages. + (RNBRemote::~RNBRemote): Ditto. + (RNBRemote::SendPacket): Ditto. + (RNBRemote::GetPacketPayload): Ditto. + (RNBRemote::GetPacket): Ditto): Ditto. + (RNBRemote::HandleAsyncPacket): Ditto. + (RNBRemote::HandleReceivedPacket): Ditto. + (RNBRemote::CommDataReceived): Ditto. + * DNB.cpp (DNBProcessLaunch): Changed to take a eror string pointer with + size for more desciptive error reporting (instead of a uint32_t pointer). + * DNB.h (DNBProcessLaunch): Ditto. + * DNBError.cpp (DNBError::AsString): Now returns NULL if there is no error. + * DNBError.h (DNBError::SetErrorString): New accessor to allow custom error + strings. + * arm/DNBArchImpl.cpp (DNBArchMachARM::GetGPRState): Improved logging. + * MachProcess.cpp (MachProcess::SBForkChildForPTraceDebugging): Improved + error messages when a file doesn't exist, or when unable to extract the + CFBundleIdentifier. + * PThreadEvent.cpp (class PThreadEvent): Commented out all logging calls. + +2009-03-07 Greg Clayton + + * test-remotenub.cpp (GetAllProcessInfosMatchingName): New function that + returns matching kinfo_proc structs given a process name. + (main): Enhanced the --attach option to be able to take a PROCNAME or + a PID. Changed the --waitfor=PROCNAME option to ignore any existing + processes with PROCNAME so we only catch new process invocations. + +2009-03-07 Greg Clayton + + * RNBRemote.cpp (RNBRemote::HandlePacket_p): Use the correct get current + thread function call so we get the correct thread registers. + +2009-03-03 Greg Clayton + + * test-remotenub.cpp (g_isatty): New global that gets set to non-zero if + STDOUT is a TTY in the beginning of main. + (RNBLogSTDOUT): New macro that logs to STDOUT if g_isatty is non-zero, else + it logs to asl. + (RNBLogSTDERR): New macro that logs to STDERR if g_isatty is non-zero, else + it logs to asl. + (RNBRunLoopGetArgsFromRemote): Use new RNBLogSTDOUT/RNBLogSTDERR macros. + (GetAllProcessInfos): Get all process info structs for everything on the + system. + (main): Implemented new --waitfor=NAME option to allow waiting for a process + to run by polling the system processes. The new --waitfor-interval=N option + allows fine control over the polling interval where N is the number of mirco + seconds (usec) to wait between polls (defaults to 1000). The new + --waitfor-duration=N allows a timeout in seconds to be specified when + waiting for a process (defaults to infinite). + +2009-03-02 Greg Clayton + + * DNBArchImpl.cpp (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): + Take care of a case where no instructions execute in a Thumb IT block and + the last of which is a branch. + +2009-02-10 Greg Clayton + + * RNBRemote.h (PacketEnum): Added 'detach' enumeration. + (RNBRemote::HandlePacket_D): New member function prototype. + * RNBRemote.cpp (RNBRemote::CreatePacketTable): Added detach support. + (RNBRemote::HandlePacket_D): New function for detach support. + +2009-02-10 Greg Clayton + + * RNBRemote.cpp (RNBRemote::HandlePacket_UNIMPLEMENTED): Log this + packet with the packet that is unimplemented. + (RNBRemote::GetPacket): Call RNBRemote::HandlePacket_UNIMPLEMENTED() + when we don't recognize a packet. + (RNBRemote::HandleReceivedPacket): Don't reply to packets we don't + recognize with unimplemented in this fucntion as that should have + already been done for us in RNBRemote::GetPacket(). + +2009-02-10 Greg Clayton + + * RNBRemote.h (PacketEnum): Added query_step_packet_supported. + * RNBRemot.cpp (RNBRemote::CreatePacketTable): Added new + qStepPacketSupported packet. + (RNBRemote::HandlePacket_q): Added support for the new + "qStepPacketSupported" packet. + (RNBRemote::HandlePacket_G): Some cleanup when reading registers + to avoid spurious console logging. + +2009-01-30 Greg Clayton + + * debugserver-entitlements.plist: Changed the entitlement + "run-invalid-allow" to "run-unsigned-code". + +2009-01-23 Greg Clayton + + * DNBArchImpl.cpp (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): + Merged Yusuf's changes to make software single stepping work. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Call new + DNBResolveExecutablePath function to resolve executable paths. + * DNB.h (DNBResolveExecutablePath): New function prototype. + * DNB.cpp (DNBResolveExecutablePath): New function that will resolve + relative paths and also executable paths for executables that aren't relative + but yet are in the shell PATH environment variable. + +2009-01-22 Greg Clayton + + * DNBArchImpl.h (class DBNArchMachARM): Renamed member variable + m_chained_hw_single_step_addr to m_hw_single_chained_step_addr. Added + new member variables: m_sw_single_step_itblock_break_id, m_last_decode_pc, + and m_sw_single_step_itblock_break_count. Renamed m_thumbStaticData to + m_last_decode_thumb, and renamed m_decodedInstruction to m_last_decode_arm. + (DBNArchMachARM::DecodeITBlockInstructions): New prototype. + (DBNArchMachARM::DecodeInstructionUsingDisassembler): New prototype. + (DBNArchMachARM::BreakpointHit): New prototype. + * DNBArchImpl.cpp (DNBArchMachARM::ThreadDidStop): Disable any of the + many software single step breakpoints if any are set. + (DNBArchMachARM::StepNotComplete): Changed renamed member accesses. + (DNBArchMachARM::DecodeITBlockInstructions): New function for software + single stepping through Thumb IT blocks. + (DNBArchMachARM::EnableHardwareSingleStep): Cleaned up logging. + (DNBArchMachARM::ComputeNextPC): Ditto. + (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): Now + properly handles Thumb IT software single stepping. + (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Ditto. + (DNBArchMachARM::DecodeInstructionUsingDisassembler): New function. + (DNBArchMachARM::BreakpointHit): New breakpoint callback function. + +2009-01-21 Greg Clayton + + * MachProcess.cpp (MachProcess::PrivateResume): Set the process state before + we actually resume so we are sure to get the events in the correct order. + +2009-01-16 Greg Clayton + + * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): Include only + registers which are to be expedited in the T packets. + (RNBRemote::HandlePacket_p): Enable for all targets. + (struct register_map_entry): Added an expedite member so we know which + registers need to be sent up to the host with each stop reply packet. + (register_map): Updated each array members' expedite member with an + appropriate value. + +2009-01-16 Greg Clayton + + * RNBRemote.cpp (RNBRemote::HandlePacket_s): Enabled the step command ("s" + packet) for ARM now that libdebugnub.dylib can do both hardware and software + single stepping. + +2009-01-13 Greg Clayton + + *DNBArchImpl.cpp (bit): New function. + (bits): New function. + (DNBArchMachARM::ConditionPassed): Use new "bit" function. + (DNBArchMachARM::ComputeNextPC): Use new "bit" function, remove inline + assembly for "RSC" instruction so this compiles for armv7 (which defaults + to thumb) + (DNBArchMachARM::NumSupportedHardwareBreakpoints): Use new "bits" function. + (DNBArchMachARM::NumSupportedHardwareWatchpoints): Use new "bits" function. + +2009-01-12 Greg Clayton + + * DNBArch.h (DNBArchProtocol::NumSupportedHardwareBreakpoints()): Removed + the "const" qualifier to allow arches to auto detect how many hardware + breakpoints they have. + (DNBArchProtocol::NumSupportedHardwareWatchpoints()): Removed the "const" + qualifier to allow arches to auto detect how many hardware watchpoints they + have. + * DNBArchImpl.h (DNBArchMachARM::NumSupportedHardwareBreakpoints()): Auto + detect how many BRP pairs are avialable and disable for armv7 for the time + being (rdar://problem/6372672). + (DNBArchMachARM::NumSupportedHardwareWatchpoints()): Auto detect how many + WRP pairs are avialable and disable for armv7 for the time being + (rdar://problem/6372672). + +2009-01-09 Greg Clayton + + * test-remotenub.cpp (main): Filled in short argument versions for + --applist (-t) and --lockdown (-k) options. + * DNBArchImpl.h (DNBArchMachARM::ConditionPassed): New protected + member function. + (DNBArchMachARM::ComputeNextPC): New protected member function. + (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): New + protected member function. + (DNBArchMachARM::m_thumbStaticData): New protected member variable. + (DNBArchMachARM::m_decodedInstruction): New protected member variable. + * DNBArchImpl.cpp (DNBArchMachARM::ThreadDidStop): Added extra code that + will log and exit when we are verifying software single stepping (a + compile time option). + (DNBArchMachARM::ConditionPassed): New function. + (DNBArchMachARM::ComputeNextPC): New function. + (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): New + function. + (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Added the guts of the + software single stepping. + (DNBArchMachARM::NumSupportedHardwareBreakpoints): Prepared for adding + auto detection code. + (DNBArchMachARM::NumSupportedHardwareWatchpoints): Prepared for adding + auto detection code. + +2008-12-11 Greg Clayton + + * DNB.h (DNBProcessWaitForEvent): Renamed to DNBProcessWaitForEvents. + (DNBProcessSetEvents): Removed (deprecated). + (DNBProcessGetWaitForResetMask): Removed (unused). + (DNBProcessSetWaitForResetMask): Removed (unused). + (DNBProcessInterruptEvents): New function prototype. + * DNB.cpp (DNBProcessWaitForEvent): Renamed to DNBProcessWaitForEvents. + (DNBProcessSetEvents): Removed (deprecated). + (DNBProcessGetWaitForResetMask): Removed (unused). + (DNBProcessSetWaitForResetMask): Removed (unused). + (DNBProcessInterruptEvents): New function that can be used to + asynchronously interrupt infinite wait for events calls. + RNBRemote.cpp (RNBRemote::HandlePacket_v): Call DNBProcessWaitForEvents. + RNBContext.cpp (RNBContext::ThreadFunctionProcessStatus): Ditto. + test-remotenub.cpp (RNBRunLoopLaunchInferior): Ditto. + (RNBRunLoopLaunchAttaching): Ditto. + +2008-12-11 Greg Clayton + + * DNB.cpp (GetProcessMap): Use new PTHREAD_MUTEX_LOCKER macro to ease + debugging of deadlocks. + (DNBProcessLaunch): Improved logging. + (DNBProcessMemoryRead): Call MachProcess::ReadMemory so breakpoint + opcodes can be removed from memory. + (DNBProcessMemoryWrite): Call MachProcess::WriteMemory so that we work + around enabled software breakpoint traps. + * DNBLog.cpp (GetLogThreadedMutex): New function. + (_DNBLogThreaded): Use new PTHREAD_MUTEX_LOCKER macro to ease + debugging of deadlocks. + (_DNBLogThreadedIf): Ditto. + * DNBBreakpoint.h (DNBBreakpoint::IntersectsRange): New function. + * DNBBreakpoint.cpp (DNBBreakpointList::FindIDByAddress): Improved + logging. + * MacOSX/MachThread.cpp (MachThread::MachThread): Improved logging. + (MachThread::~MachThread): Ditto. + (MachThread::Suspend): Ditto. + (MachThread::Resume): Ditto. + (MachThread::RestoreSuspendCount): Ditto. + (MachThread::GetState): Use new PTHREAD_MUTEX_LOCKER macro to ease + debugging of deadlocks. + (MachThread::SetState): Ditto. + * MacOSX/MachVMMemory.cpp (MachVMMemory::Read): Improved logging. + (MachVMMemory::Write): Ditto. + (MachVMMemory::WriteRegion): Ditto. + * MacOSX/MachProcess.cpp (MachProcess::GetState): Use new + PTHREAD_MUTEX_LOCKER macro to ease debugging of deadlocks. + (MachProcess::SetState): Ditto. + (MachProcess::Clear): Ditto. + (MachProcess::PrivateResume): Ditto. + (MachProcess::ReplyToAllExceptions): Ditto. + (MachProcess::ExceptionMessageReceived): Ditto. + (MachProcess::AppendSTDOUT): Ditto. + (MachProcess::GetAvailableSTDOUT): Ditto. + (MachProcess::ThreadFunctionSTDIO): Renamed from to + MachProcess::STDIOThread. + (MachProcess::StartSTDIOThread): Improved logging. + (MachProcess::CreateBreakpoint): Ditto. + (MachProcess::CreateWatchpoint): Ditto. + (MachProcess::DisableAllBreakpoints): Ditto. + (MachProcess::DisableBreakpoint): Ditto. + (MachProcess::DisableWatchpoint): Ditto. + (MachProcess::EnableBreakpoint): Ditto. + (MachProcess::EnableWatchpoint): Ditto. + (MachProcess::LaunchForDebug): Ditto. + (MachProcess::PosixSpawnChildForPTraceDebugging): Ditto. + (MachProcess::Detach): Reset the running event bit after resuming prior + to issuing the SIGSTOP to avoid a pause. + (MachProcess::RemoveTrapsFromBuffer): New function that removes + breakpoint traps from a memory buffer. + (MachProcess::ReadMemory): Read memory from the task, then removes any + breakpoint traps prior to returning the buffer. + (MachProcess::WriteMemory): Write memory and any needed data to the + breakpoint saved opcodes for any software breakpoint traps that are + enabled. + * MacOSX/MachProcess.h (MachProcess::ThreadFunctionException): Removed. + (MachProcess::ThreadFunctionSTDIO): Renamed to MachProcess::STDIOThread(). + (MachProcess::RemoveTrapsFromBuffer): New function. + * MacOSX/MachVMRegion.cpp (MachVMRegion::SetProtections): Improved + logging. + (MachVMRegion::RestoreProtections): Ditto. + (MachVMRegion::GetRegionForAddress): Ditto. + * MacOSX/MachException.cpp (catch_mach_exception_raise_state): Improved + logging. + (catch_mach_exception_raise_state_identity): Ditto. + (catch_mach_exception_raise): Ditto. + (MachException::Message::Dump): Ditto. + (MachException::Data::GetStopInfo): Ditto. + (MachException::Message::Receive): Ditto. + (MachException::Message::Reply): Ditto. + (MachException::Data::Dump): Ditto. + (MachException::PortInfo::Save): Ditto. + (MachException::PortInfo::Restore): Ditto. + * MacOSX/MachTask.cpp (MachTask::Suspend): Improved logging. + (MachTask::Resume): Ditto. + (MachTask::ReadMemory): Ditto. + (MachTask::WriteMemory): Ditto. + (MachTask::TaskPortForProcessID): Ditto. + (MachTask::BasicInfo): Ditto. + (MachTask::StartExceptionThread): Ditto. + (MachTask::ShutDownExcecptionThread): Ditto and use pthread_cancel to + interrupt the exception thread. + (MachTask::ExceptionThread): Ditto and revert back to infinite timeout + as pthread_cancel will break us out of infinite mach_msg receive calls. + * MacOSX/MachThreadList.cpp (MachThreadList::UpdateThreadList): Improved + logging. + (MachThreadList::CurrentThread): Use new PTHREAD_MUTEX_LOCKER macro to + ease debugging of deadlocks. + * DNBTimer.h (DNBTimer::DNBTimer): Initialize the mutex with a recursive + pthread. + (DNBTimer::Reset): Use new PTHREAD_MUTEX_LOCKER macro to ease debugging + of deadlocks. + (DNBTimer::TotalMicroSeconds): Ditto. + (DNBTimer::GetTime): Ditto. + (DNBTimer::ElapsedMicroSeconds): Ditto. + (DNBTimer::GetTimeOfDay): New class function. + * DNBError.cpp (DNBError::LogThreaded): Improved logging. + * test-dbgnub.cpp + * PThreadMutex.h: Added the ability to debug deadlocks by defining + DEBUG_PTHREAD_MUTEX_DEADLOCKS. + * FunctionProfiler.cpp + * PThreadEvent.cpp (PThreadEvent::NewEventBit): Use new + PTHREAD_MUTEX_LOCKER macro to ease debugging of deadlocks. + (PThreadEvent::FreeEventBits): Ditto. + (PThreadEvent::GetEventBits): Ditto. + (PThreadEvent::ReplaceEventBits): Ditto. + (PThreadEvent::SetEvents): Ditto. + (PThreadEvent::ResetEvents): Ditto. + (PThreadEvent::WaitForSetEvents): Ditto. + (PThreadEvent::WaitForEventsToReset): Ditto. + +2008-12-05 Greg Clayton + + * DNBDefs.h (LOG_TASK): New log bit. + * DNB.cpp (DNBProcessIsAlive): User newly abtracted MachTask class. + (DNBProcessMemoryRead): Ditto. + (DNBProcessMemoryWrite): Ditto. + * DNBArchImpl.cpp (DNBArchMachARM::EnableHardwareSingleStep): Ditto. + (DNBArchMachARM::SetSingleStepSoftwareBreakpoints) Ditto. + * MachException.cpp (MachException::Message::Receive): Cleaned up logging + so it doesn't always log timeout errors. + (MachException::Message::Reply): Use abstracted MachTask class for any + task related queries. + (MachException::PortInfo::Save): Cleaned up logging. + (MachException::PortInfo::Restore): Cleaned up logging and now return an + error instead of the number of restored port infos. + * MachProcess.cpp (class MachProcess): Abstracted out all of the task_t + related stuff (suspend, resuyme, exception ports, exception thread, and + more) into a new class MachTask. + (MachProcess::Task): Now returns a reference to a MachTask class. + (MachProcess::Clear): Uses new abstracted MachTask class. + (MachProcess::Detach): Ditto. + (MachProcess::PrivateResume): Ditto. + (MachProcess::DisableBreakpoint): Ditto. + (MachProcess::ExceptionMessageReceived): Ditto. + (MachProcess::ExceptionMessageBundleComplete): Ditto. + (MachProcess::AttachForDebug): Ditto. + (MachProcess::LaunchForDebug): Ditto. + (MachProcess::SBLaunchForDebug): Ditto. + (MachProcess::TaskIsValid): Removed (replaced by similar functionality + in the new MachTask class). + (MachProcess::ExceptionPort): Ditto. + (MachProcess::ExceptionPortIsValid): Ditto. + (MachProcess::StartExceptionThread): Ditto. + (MachProcess::Suspend): Ditto. + (MachProcess::TaskResume): Ditto. + (MachProcess::TaskBasicInfo): Ditto. + (MachProcess::TaskBasicInfo): Ditto. + (MachProcess::ReadMemory): Ditto. + (MachProcess::WriteMemory): Ditto. + (MachProcess::ThreadFunctionException): Ditto. + +2008-12-04 Greg Clayton + + * DNB.h (DNBProcessSetEvents): New API function prototype. + * DNB.cpp (DNBProcessSetEvents): New API function. + (DNBProcessHalt): Send our process a SIGINT instead of suspending + the task. + * DNBDefs.h (NUB_STATE_IS_STOPPED): Removed up duplicate entry in macro. + (eEventPrcoessAsyncInterrupt): New prcoess event bit that allows async + interrupting of infinite DNBProcessWaitForEvent() function calls. + * MachException.cpp (MachException::Message::Receive): Improved logging. + (MachException::Message::Reply): Improved logging. + * MachProcess.h (MachProcess::TaskBasicInfo): New member and static + functions. + * MachProcess.cpp (MachProcess::TaskIsValid): Use new TaskBasicInfo() + member function. + (MachProcess::Resume): Removed the detach parameter from the PrivateResume() + function call. + (MachProcess::Kill): Added a absolute timeout pointer to allow callers to + wait for the signal to be received if the timeout is non-NULL. + (MachProcess::TaskBasicInfo): New member and static function. + (MachProcess::TaskResume): New function that resumes the task by making sure + the suspend count is correctly ref counted. + (MachProcess::Detach): When detaching from a process make sure it is + stopped (SIGSTOP) first, then we can successfully detach. The exception + thread now also properly exits. + (MachProcess::PrivateResume): Call new TaskResume function, and removed the + detach functionality. + (MachProcess::DisableBreakpoint): Only notify the thread list that a + breakpoint has changed if the breakpoint is going to be removed. + (MachProcess::ThreadFunctionException): Added a permanent 1 second timeout + for each call to mach_msg() so we can exit the thread in the event that + we detach from a process/task. + * test-debugnub (main): Modified to show an example of how to detach using + a signal_handler to asynchronously receive a SIGINT and properly interrupt + and detach from a running process. + +2008-11-26 Greg Clayton + + * DNBDefs.h (LOG_STEP): New logging define. + * DNBError.cpp (DNBError::LogThreaded): If there is no error, then + log with "success: " as a prefix instead of "error: ". + * arm/DBNArchImpl.cpp (DNBArchMachARM::EnableHardwareSingleStep): Log using + new LOG_STEP instead of LOG_BREAKPOINTS. + (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Ditto. + * MachException.cpp (MachException::Message::Dump): Log excetion header + and reply header on two separate lines. + * MachProcess.cpp (IsSBProcess): Check for NULL CFArrayRef returned from + SBSCopyApplicationDisplayIdentifiers for SkankPhone. + (MachProcess::Suspend): Check if process state is not running instead of + having to receive an event after a timeout if one is given. + (MachProcess::Detach): Deallocate the exception port when detaching and + restore the inferior task exception ports prior to clearing and detaching. + (MachProcess::PrivateResume): Grab the task's basic info and make sure we + get the resume the correct number of times. + (MachProcess::DisableBreakpoint): Removed unused variable opcode_restored + and make sure the breakpoint is enabled before we start warning that + our opcode wasn't there. + * ppc/DBNArchImpl.cpp (DNBArchMachPPC::EnableHardwareSingleStep): Log + using LOG_STEP instead of LOAD_BREAKPOINTS. + * RNBServices.cpp (IsSBProcess): Check for NULL CFArrayRef returned from + SBSCopyApplicationDisplayIdentifiers for SkankPhone. + +2008-11-26 Greg Clayton + + * MachProcess.h (MachProcess::Suspend): Now takes an optional absolute + timeout that, if non-NULL, will case the function to return after the + process has been suspended and is in a stopped state. If the timeout is + NULL, then no waiting will occur. + * MachProcess.cpp (MachProcess::Suspend): Ditto. + (MachProcess::Detach): Now replies to all exceptions, un-suspends all + threads and resumes the task. + (MachProcess::ReplyToAllExceptions): New function. + (MachProcess::PrivateResume): Now takes an additional parameter named + detach that will do the right thing when detaching from a process. + * DNBArchImpl.h (DNBArchMachI386::ThreadWillResume): Returns void. + * DNBArchImpl.cpp (DNBArchMachI386::ThreadWillResume): Returns void. + * RNBServices.cpp (ListApplications): #ifdef-ed for ARM only as it + currently uses SpringBoard. + (IsSBProcess): Ditto. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): #ifdef-ed around + ARM parts so it compiles for i386. + (main): Ditto. + +2008-11-24 Greg Clayton + + * DNBArchProtocol.h (DNBArchProtocol::ThreadWillResume): Now returns void. + * DNBArchImpl.cpp (DNBArchMachARM::ThreadWillResume): Returns void and + has hollowed out support for software single step. + (DNBArchMachARM::ThreadDidStop): Has a debug mode that uses hardware single + step to verify software single step that can be enabled by defining + DNB_ARCH_MACH_ARM_DEBUG_SW_STEP. + (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): New function. + * DNBArchImpl.h (DNBArchMachARM::ThreadWillResume): Returns void. + (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): New prototype. + (DNBArchMachARM::m_sw_single_step_next_pc): New member variable. + (DNBArchMachARM::m_sw_single_step_break_id): New member variable. + * MachThread.cpp (MachThread::ThreadWillResume): Now returns void. + * MachThread.h (MachThread::ThreadWillResume): Now returns void. + +2008-11-19 Greg Clayton + + * DNBError.h (FlavorType): Added SpringBoard error type for arm builds. + * DNBError.cpp (DNBError::AsString): Now returns SpringBoard error strings + if the error type is SpringBoard. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Set the error into + RNBContext as either a POSIX error or a SpringBoard error. + * RNBContext.h (m_launch_status): Changed this member to be a DNBError + instead of a uint32_t. + (RNBContext::LaunchStatus): Now returns a reference to the DNBError object + in m_launch_status. + * RNBContext.cpp (RNBContext::LaunchStatusAsString): Let DNBError handle + any error string descriptions, including SpringBoard errors. + * RNBRemote.cpp (RNBRemote::HandlePacket_q): Use new error class in + RNBContext. + (RNBRemote::HandlePacket_C): Return without an erroneous error when resuming + a process with a signal. + * DNBArch.h (DNBArchProtocol::StepNotComplete): New protocol function with + default return value. + * DNBArchImpl.cpp (DNBArchMachARM::StepNotComplete): New function. + (DNBArchMachARM::EnableHardwareSingleStep): Handle hardware single stepping + over 32 bit thumb instructions better so we always do a true instruction + level single step. + * MachProcess.cpp (MachProcess::ExceptionMessageBundleComplete): Now resumes + if single stepping wasn't able to complete in a single run. + * MachThread.cpp (MachThread::ShouldStop): Fills in new step_more parameter + if stepping is not complete. + * MachThreadList.cpp (MachThreadList::ShouldStop): Pass step_more parameter + to each MachThread::ShouldStop call. + +2008-11-13 Greg Clayton + + * MachProcess.cpp (MachProcess::PosixSpawnChildForPTraceDebugging): Don't + call posix_spawnattr_setbinpref_np when launching with posix_spawn on ARM + targets as it currently selects the incorrect slice due to multiple slices + that contain the same cputype, yet they all have differing cpusubtypes. + +2008-11-04 Greg Clayton + + * RNBRemote.h (GetContinueThread): Don't return the current thread when + the continue thread is zero or -1. + * RNBRemote.cpp (RNBRemote::HandlePacket_c): Resume the process if we + have no continue thread set. + (RNBRemote::HandlePacket_s): Ditto. + (RNBRemote::HandlePacket_C): Ditto unless a continue address is specified + in which case we will only succeed if we have one thread when the continue + with signal and address doesn't have a continue thread specified. + (RNBRemote::HandlePacket_S): Ditto. + * DNB.cpp (DNBProcessResumeWithSignal): New function. + (DNBProcessResume): Added better logging. + (DNBProcessHalt): Ditto. + (DNBThreadResume): Ditto. + (DNBThreadResumeWithSignal): Ditto. + * DNB.h (DNBProcessResumeWithSignal): New prototype. + * DNBError.cpp (DNBError::LogThreaded): New function. + * DNBError.h (DNBError::LogThreaded): New prototype. + * DNBLog.cpp (_DNBLogThreaded): Added sequence ID for threaded logs. + (_DNBLogThreadedIf): Ditto. + * MachException.cpp (MachException::Data::GetStopInfo): Use new SoftSignal() + accessor. + (MachException::Data::DumpStopReason): Ditto. + (MachException::Message::Reply): Added better logging and log using the + soft signal if our task matches that in the exception. + (MachException::Data::Dump): Added better logging. + * MachException.h (IsSoftSignal): Removed. + (SoftSignal): New function that returns the soft signal in the exception + data if there is one, or zero otherwise. + * MachProcess.cpp (MachProcess::Suspend): Improved logging. + (MachProcess::Resume): Ditto. + (MachProcess::PrivateResume): Handle the case where the process is told + to resume with a signal by matching the signal up to the thread that had + the soft signal if no thread id is specified. + * MachThread.cpp (MachThread::Suspend): Improved logging. + (MachThread::Resume): Improved logging. + (MachThread::RestoreSuspendCount): Improved logging. + (MachThread::Resume): Improved logging. + (MachThread::Dump): Improved logging. + * MachThreadList.cpp (MachThreadList::Dump): Improved logging. + +2008-10-22 Greg Clayton + + * test-remotenub.cpp (RNBRunLoopMode): Added a new enum value + eRNBRunLoopModeInferiorAttaching. + (g_long_options): Added "--attach=PID" for attaching to existing processes + and "--launch=(auto|posix|fork|springboard)" options. + (RNBRunLoopLaunchInferior): Now launches process with new + nub_launch_flavor_t enum that can be overridden with the --launch option. + (RNBRunLoopLaunchAttaching): New function for attaching to existing + processes. + (main): Added command line option support for the "--attach" and "--launch" + options and added attach to pid support and better logging. + * DNB.cpp/h: (DNBProcessLaunch): Added nub_launch_flavor_t and error + parameter for more precise control when launching processes. + (DNBProcessSBLaunch): Removed function as launching with SpringBoard can + now be done using DNBProcessLaunch with launch_flavor being set to + eLaunchTypeSpringBoard (arm only). + (DNBProcessSBAttach): Removed function (SpringBoard processes are now auto + detected in the MachProcess::AttachForDebug function on ARM). + * DNBDefs.h (NUB_GENERIC_ERROR): New generic error definition. + (nub_launch_flavor_t): New enumeration used for control over process + launching. + * MachProcess.cpp (IsSBProcess): New function. + (MachProcess::AttachForDebug): Removed flags parameter that was being used + for SpringBoard flags and we now detect if a process belongs to SpringBoard + by calling IsSBProcess. + (MachProcess::LaunchForDebug): Now has launch parameter that tells it how + to launch the inferior process and there is also an error code that gets + returned. This function can now launch using fork + exec, posix_spawn, + or SpringBoard on ARM targets. + (MachProcess::SBLaunchForDebug): Now uses DNBError reference instead of + uint32_t pointer for the error code. + (MachProcess::SBForkChildForPTraceDebugging): Ditto. + +2008-10-22 Greg Clayton + + * MacOSX/arm/DNBArchImpl.cpp (DNBArchMachARM::GetRegisterValue): Set + register value to a uint32 value instead of a float64 value for s0 - + s31. + +2008-10-17 Greg Clayton + + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Don't listen for + the qLaunchSuccess if we aren't doing a lockdown connnection. + +2008-10-13 Greg Clayton + + * RNBRemote.h (class RNBRemote): Added m_watchpoints member. + * DNB.cpp (DNBBreakpointSet): Added boolean hardware parameter for + requesting that a hardware breakpoint be set. + (DNBWatchpointSet): New function. + (DNBWatchpointClear): New function. + (DNBWatchpointGetHitCount): New function. + (DNBWatchpointGetIgnoreCount): New function. + (DNBWatchpointSetIgnoreCount): New function. + (DNBWatchpointSetCallback): New function. + (DNBWatchpointPrint): New function. + * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Modified to emit + a single DNBLog() call so there aren't multiple newlines when logging + to ASL. + * RNBContext.cpp (RNBContext::ThreadFunctionProcessStatus): Use new + process state changed events. + * DNBBreakpoint.h (class DNBBreakpoint): Removed m_state member and + added m_tid, m_enabled, m_hw_preferred, m_is_watchpoint, m_watch_read, + m_watch_write, and m_hw_index. + (DNBBreakpoint::ThreadID()): New accessor. + (DNBBreakpoint::IsEnabled()): New accessor. + (DNBBreakpoint::SetEnabled()): New accessor. + (DNBBreakpoint::IsWatchpoint()): New accessor. + (DNBBreakpoint::IsBreakpoint()): New accessor. + (DNBBreakpoint::SetIsWatchpoint()): New accessor. + (DNBBreakpoint::WatchpointRead()): New accessor. + (DNBBreakpoint::WatchpointWrite()): New accessor. + (DNBBreakpoint::HardwarePreferred()): New accessor. + (DNBBreakpoint::IsHardware()): New accessor. + (DNBBreakpoint::GetHardwareIndex()): New accessor. + (DNBBreakpoint::SetHardwareIndex()): New accessor. + (DNBBreakpoint::ThreadID()): New accessor. + (DNBBreakpoint::GetState()): Removed accessor. + (DNBBreakpoint::SetState()): Removed accessor. + (DNBBreakpoint::AddBreakpoint()): Renamed to Add(). + (DNBBreakpoint::RemoveBreakpoint()): Renamed to Remove(). + (DNBBreakpoint::FindBreakIDForAddress()): Renamed to FindIDByAddress(). + (DNBBreakpoint::ShouldStopAtBreakpoint()): Renamed to ShouldStop(). + (DNBBreakpoint::SetBreakpointCallback()): Renamed to SetCallback(). + (DNBBreakpoint::FindBreakpointWithAddress()): Renamed to + FindByAddress(). + (DNBBreakpoint::FindBreakpointWithBreakID()): Renamed to FindByID(). + (DNBBreakpoint::GetBreakpointAtIndex()): Renamed to GetByIndex(). + * FunctionProfiler.h: New header for subclass of DNBRuntimeAction. + * RNBRemote.cpp (RNBRemote::HandlePacket_v): Use new process state + changed events. + (RNBRemote::HandlePacket_z): Implement the hardware breakpoint and + watchpoint commands z1, Z1, z2, Z2, z3 and Z3 + * PThreadEvent.h (PThreadEvent::GetEventBits): Made member function + const. + (PThreadEvent::WaitForSetEvents): Ditto. + (PThreadEvent::WaitForEventsToReset): Ditto. + (PThreadEvent::WaitForResetAck): Ditto. + (PThreadEvent::m_mutex): Made class member mutable. + (PThreadEvent::m_set_condition): Made class member mutable. + (PThreadEvent::m_reset_condition): New mutable class member. + * ProfileObjectiveC.cpp + * DNBArch.h (DNBArch::NotifyException): Now has default implementation + that returns false. + (DNBArch::NumSupportedHardwareBreakpoints): New virtual member + function with a default implementation. + (DNBArch::NumSupportedHardwareWatchpoints): Ditto. + (DNBArch::EnableHardwareBreakpoint): Ditto. + (DNBArch::EnableHardwareWatchpoint): Ditto. + (DNBArch::DisableHardwareBreakpoint): Ditto. + (DNBArch::DisableHardwareWatchpoint): Ditto. + * DNB.h (DNBBreakpointSet): New take a HARDWARE parameter that allows + requests for setting hardware breakpoints. + (DNBWatchpointSet): New function prototype. + (DNBWatchpointClear): New function prototype. + (DNBWatchpointGetHitCount): New function prototype. + (DNBWatchpointGetIgnoreCount): New function prototype. + (DNBWatchpointSetIgnoreCount): New function prototype. + (DNBWatchpointSetCallback): New function prototype. + (DNBWatchpointPrint): New function prototype. + * MacOSX/arm/DNBArchImpl.cpp: Added hardware breakpoint and watchpoint + support for ARM. + (DNBArchMachARM::GetCPUType): New function. + (DNBArchMachARM::DumpDBGState): New function. + (DNBArchMachARM::GetDBGState): New function. + (DNBArchMachARM::SetDBGState): New function. + (DNBArchMachARM::EnableHardwareSingleStep): New function. + (DNBArchMachARM::EnableHardwareBreakpoint): New function. + (DNBArchMachARM::NotifyException): Removed. + (DNBArchMachARM::DisableHardwareBreakpoint): New function. + (DNBArchMachARM::EnableHardwareWatchpoint): New function. + (DNBArchMachARM::DisableHardwareWatchpoint): New function. + * MacOSX/MachThread.cpp (MachThread::Suspend): Added better logging. + (MachThread::Resume): Ditto. + (MachThread::RestoreSuspendCount): Ditto. + (MachThread::Dump): Ditto. + (MachThread::EnableHardwareBreakpoint): New function. + (MachThread::EnableHardwareWatchpoint): New function. + (MachThread::DisableHardwareBreakpoint): New function. + (MachThread::DisableHardwareWatchpoint): New function. + * MacOSX/MachThreadList.h (MachThreadList::GetLastError): Removed. + (MachThread::EnableHardwareBreakpoint): New prototype. + (MachThread::DisableHardwareBreakpoint): New prototype. + (MachThread::EnableHardwareWatchpoint): New prototype. + (MachThread::DisableHardwareWatchpoint): New prototype. + (class MachThread): Remove m_err member variable. + * MacOSX/ppc/DNBArchImpl.cpp (DNBArchMachPPC::GetCPUType) New + function. + (DNBArchMachPPC::NotifyException): Removed. + * MacOSX/ppc/DNBArchImpl.h (DNBArchMachPPC::NotifyException): Removed. + * MacOSX/MachThread.h (MachThread::EnableHardwareBreakpoint): New + prototype. + (MachThread::EnableHardwareWatchpoint): New prototype. + (MachThread::DisableHardwareBreakpoint): New prototype. + (MachThread::DisableHardwareWatchpoint): New prototype. + (class MachThread): Renambed class member m_exception to + m_stop_exception. + * MacOSX/MachProcess.cpp (MachProcess::SetState): Updated to use new + process event enumerations. + (MachProcess::PrivateResume): Added better logging. + (MachProcess::CreateBreakpoint): Added bool HARDWARE parameter for + requesting hardware breakpoints. + (MachProcess::CreateWatchpoint): New function. + (MachProcess::DisableAllWatchpoints): New function. + (MachProcess::DisableWatchpoint): New function. + (MachProcess::DumpWatchpoint): New function. + (MachProcess::EnableBreakpoint): Enabled breakpoints in hardware if + requested and supported. + (MachProcess::DisableBreakpoint): Disable hardware breakpoints if that + is how they were set. + (MachProcess::EnableWatchpoint): New function. + (MachProcess::ExceptionMessageBundleComplete): Wait for the + eEventProcessRunningStateChanged event to be reset before changing + state to stopped to avoid race condition with very fast start/stops. + (MachProcess::LaunchForDebug): Added posix_spawn support. + (MachProcess::PosixSpawnChildForPTraceDebugging): New function. + * MacOSX/i386/DNBArchImpl.cpp (DNBArchMachI386::GetCPUType): New + function. + * MacOSX/i386/DNBArchImpl.h (DNBArchMachI386::GetCPUType): New + prototype. + * MacOSX/MachProcess.h (PosixSpawnChildForPTraceDebugging): New + prototype. + * MacOSX/MachException.cpp (class MachException::ThreadMessage): + Renamed class to MachException::Data. + * MacOSX/MachThreadList.cpp (class MachThreadList): Removed m_err + class member. + (MachThreadList::EnableHardwareBreakpoint): New function. + (MachThreadList::DisableHardwareBreakpoint): New function. + (MachThreadList::EnableHardwareWatchpoint): New function. + (MachThreadList::DisableHardwareWatchpoint): New function. + * MacOSX/MachException.h (class MachException::ThreadMessage): + Renamed class to MachException::Data. + * DNBDefs.h (nub_watch_t): New typedef. + (INVALID_NUB_HW_INDEX): New macro definition. + (WATCH_TYPE_READ): New macro definition. + (WATCH_TYPE_WRITE): New macro definition. + (NUB_STATE_IS_RUNNING): New macro to see if state is a running state. + (NUB_STATE_IS_STOPPED): New macro to see if state is a stopped state. + (eEventProcessStateChanged): Deprecated. + (eEventProcessRunningStateChanged): New process event state. + (eEventProcessStoppedStateChanged): New process event state. + (LOG_WATCHPOINTS): New macro definition for logging watchpoints. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Use new process + event states. + * FunctionProfiler.cpp: New class that allows single stepping through + an address range for tracing exact call graphs. + +2008-09-22 Greg Clayton + + * RNBRemote.h (GetContinueThread): If the continue thread is zero or + -1 then return GetCurrentThread(). + * RNBRemote.cpp (m_packets): Made the vCont functions call + RNBRemote::HandlePacket_v(). + (RNBRemote::HandlePacket_H): Cleaned up whitespace. + (RNBRemote::HandlePacket_last_signal): Return actual signal values for + EXE_SOFTWARE/EXC_SOFT_SIGNAL mach exceptions. + (RNBRemote::HandlePacket_v): Implemented the 'vCont?' and 'vCont;' + packets. + (RNBRemote::HandlePacket_c): Handle the case where an address is + provided. + (RNBRemote::HandlePacket_C): Implemented the continue with signal + including when an address is provided. + (RNBRemote::HandlePacket_S): Implemented the step with signal + including when an address is provided. + * DNB.cpp (DNBProcessResume): Pass 0 as the signal when resuming + a process without specifying a thread. + (DNBThreadResume): Pass 0 as the signal when resuming a specific thread. + (DNBThreadResumeWithSignal): New function. + * DNB.h (DNBThreadResumeWithSignal): New prototype. + * MachException.h (MachException::Message::Reply): Added a signal + parameter. + * MachException.cpp (MachException::Message::Reply): Update the thread + with the new SIGNAL parameter instead of always zero so signals can be + passed on to programs. + * MachProcess.h (MachProcess::Resume): Added a signal parameter. + * MachProcess.h (MachProcess::PrivateResume): Added a signal parameter. + * MachProcess.cpp (MachProcess::Resume): Pass new SIGNAL parameter to + MachProcess::PrivateResume. + * MachProcess.cpp (MachProcess::PrivateResume): Pass new SIGNAL + parameter to the mach exception reply. + +2008-08-08 Greg Clayton + + * DNB.cpp (gProcessMap): Removed static C++ global. + (GetProcessMap): New Function. + (AddProcessToMap): New function. + (RemoveProcessFromMap): New function. + (GetProcessSP): Use new GetProcessMap function to get process list. + +2008-07-30 Greg Clayton + + * debugserver-entitlements.plist (get-task-allow): Removed. + (run-invalid-allow): Added boolean value set to TRUE. + +2008-04-18 Greg Clayton + + * MachProcess.cpp (MachProcess::Task): Added getuid(), geteuid(), + getgid(), getegid() to the log message if task for pid fails. + +2008-04-07 Greg Clayton + + * RNBContext.cpp (RNBContext::LaunchStatusAsString): Removed unused + tmp_str variable. + +2008-04-04 Greg Clayton + + * CFString.cpp/h (UTF8): Made a static function that can convert + a CFStringRef to UTF8. + +2008-04-04 Greg Clayton + + * test-remotenub.cpp (main): Make sure we exit after we send the + application list. + +2008-04-04 Greg Clayton + + * RNBServices.h (IsSBProcess): New prototype; + * RNBServices.cpp (IsSBProcess): New function that returns true it + SpringBoard owns or knows about the process. + * RNBRemote.cpp (RNBRemote::HandlePacket_v): Made attach work correctly. + * DNB.cpp (DNBProcessSBAttach): New function for use when attaching to + a process owned by SpringBoard. + (DNBProcessAttach): Fixed an issue where a local was shadowing a + parameter. + * DNB.h (DNBProcessSBAttach): New prototype. + * MachProcess.cpp (MachProcess::AttachForDebug): AttachForDebug now + takes some flags so it knows to enable SpringBoard functionality. + * MachProcess.h (MachProcess::AttachForDebug): Added flags parameter + to prototype. + +2008-04-04 Greg Clayton + + * test-remotenub.cpp (RNBRunLoopGetArgsFromRemote): Handle the new + attach packet and watch for connection being lost. + (main): handle the --applist option when there we aren't using lockdown + by printing the results to stdout and exiting with appropriate error code + if we failed. Also handle the new prototype for ListApplications. + * RNBServices.h (ListApplications): Change first parameter to be a std::string + that will get the contents of the plist so we can use this for more than + just lockdown. + * RNBServices.cpp (ListApplications): Change first parameter to be a std::string + that will get the contents of the plist so we can use this for more than + just lockdown and also fixed the logic so we actually create a full list of + applications instead of just overwriting the first entry. + * RNBRemote.h (PacketEnum): Added a new 'vattach' enum for the "vAttach;PID" + gdb remote command. + (RNBRemote::HandlePacket_v): New prototype; + * RNBRemote.cpp (RNBRemote::CreatePacketTable): add the vattach packet definition + to m_packets. + (RNBRemote::HandlePacket_v): New function that handles attach to a process. + +2008-04-03 Jim Ingham + + * RNBRemote.h: Add query_launch_success to packet enum. + * RNBRemote.cpp (RNBRemote::CreatePacketTable_): Add query_launch_success. + (HandlePacket_q): Handle query_launch_success. + * DNB.cpp (DNBProcessSBLaunch): Pass in launch_retval. + * DNB.h: Change prototype of DNBProcessSBLaunch to take launch_retval. + * RNBContext.cpp (RNBContext::LaunchStatusAsString): New function. + * RNBContext.h (RNBContext): Add m_launch_status & accessors. + * macosx/MachProcess.cpp (MachProcess::SBLaunchForDebug): Pass launch_retval. + (MachProcess::SBForkChildForPTraceDebugging): Accept & set launch_retval. + * Macosx/MachProcess.h: Change prototypes of SBLaunchForDebug & + ForkChildForPTraceDebugging to accept launch_retval. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Get the launch status and + put it in the context, then wait for the qLaunchStatus packet. + +2008-04-03 Greg Clayton + + * com.apple.debugserver.plist: Changed plist so debugserver + runs as mobile user. + * com.apple.debugserver.applist.plist: Ditto. + +2008-04-03 Greg Clayton + + * MachProcess.cpp: (MachProcess::SBForkChildForPTraceDebugging): + Increased SBS application launch timeout to 30 seconds. + +2008-03-27 Christopher Friesen + + * RNBServices.h: Pass tasks from SpringBoard as a plist + * RNBServices.cpp: Ditto. + * test-remotenub.cpp: added --applist flag + * com.apple.debugserver.applist.plist: Agent plist + +2008-03-17 Jim Ingham + + * DNB.h: Pass envp to DNBProcessLaunch & DNBProcessSBLaunch. + * DNB.cpp: Ditto. + * MachProcess.h: Ditto for *LaunchForDebug and + *ForkChildForPtraceDebugging. + * MachProcess.cpp (MachProcess::LaunchForDebug): Pass on envp. + (MachProcess::SBLaunchForDebug): Ditto. + (MachProcess::ForkChildForPtraceDebugging): Accept envp, haven't actually + implemented the passing yet. + (MachProcess::SBForkChildForPtraceDebuggin): Accept envp, convert to + CFDictionary and pass to SBSLaunchApplication. + * RNBContext.h: Add environment to the context. + * RBNContext.cpp (RNBContext::EnvironmentAtIndex): New function. + * RNBRemote.h: Add set_environment_variable to the PacketEnum. + * RNBRemote.cpp (RNBRemote::CreatePacketTable): Add QEnvironment:. + * (RNBRemote::HandlePacket_Q): Ingest the environment variable. + * test-remotenub.cpp (RNBRunLoppLaunchInferior): Convert the env + array in the context into an array, and pass it to the DNBProcess*Launch + methods. + +2008-03-17 Greg Clayton + + * DNBBreakpoint.cpp (DNBBreakpointList::GetBreakpointAtIndex): New + functions (const and non-const versions). + * DNBBreakpoint.h (DNBBreakpointList::GetBreakpointAtIndex): New + prototypes (const and non-const versions). + * DNBError.h (DNBError::Success()): Don't use KERN_SUCCESS define. + (DNBError::Fail()): Don't use KERN_SUCCESS define. + * MachProcess.cpp (MachProcess::DisableAllBreakpoints): New function. + (MachProcess::Detach): Added initial implementation that will halt + the process, disable all breakpoints and call PT_DETACH. + * MachProcess.h (MachProcess::DisableAllBreakpoints): New prototype. + +2008-03-04 Greg Clayton + + * RNBRemote.h (RNBRemote::SendHexEncodedBytePacket): New prototype. + * RNBRemote.cpp (RNBRemote::SendHexEncodedBytePacket): New function. + (RNBRemote::SendSTDOUTPacket): Use SendHexEncodedBytePacket function + to send bytes. + (RNBRemote::SendSTDERRPacket): Ditto. + (RNBRemote::HandlePacket_q): Return a valid thread info string for + qThreadExtraInfo queries. + * DNB.cpp (DNBThreadPrintStopReason): Commented out unused function. + (DNBThreadGetInfo): New function. + * DNB.h (DNBThreadPrintStopReason): Commented out prototype. + (DNBThreadGetInfo): New prototype. + * MachProcess.cpp (MachProcess::GetThreadInfo): New function. + * MachProcess.h (MachProcess::GetThreadInfo): New prototype. + * MachThreadList.cpp (MachThreadList::GetThreadInfo): New function. + * MachThreadList.h (MachThreadList::GetThreadInfo): New prototype. + * MachThread.cpp (MachThread::GetBasicInfoAsString): New function. + (MachThread::InferiorThreadID): New function. + * MachThread.cpp (MachThread::GetBasicInfoAsString): New prototype. + (MachThread::InferiorThreadID): New prototype. + +2008-02-27 Greg Clayton + + * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): Set the + current thread when we notify a thread has stopped to subsequent + g and p packets get the correct data. + +2008-02-26 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Add query_thread_extra_info enum. + * RNBRemote.cpp: Add support for qThreadExtraInfo. + Currently we return 'Ok' as the packet status for + every thread. + +2008-02-26 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (RNBRemote::HandlePacket_q): Correct handling + of qfThreadInfo/qsThreadInfo. + +2008-02-20 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Change default for gdb's max incoming packet size to + reflect the real default size. + * RNBRemote.cpp (HandlePacket_Q): Correct the string comparisions for + the QSetMaxPayloadSize and QSetMaxPacketSize packets. + +2008-02-19 Christopher Friesen + + * CFDataFormatters.c: CoreFoundation data formatters added to project. + +2008-02-19 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Record the max payload size, not the max packet + size for less ambiguous meaning. + * RNBRemote.cpp: Add support for QSetMaxPayloadSize: packet which + should have a clearer meaning than QSetMaxPacketSize. + QSetMaxPacketSize will be removed once we get have a chance to get + a new debugserver and gdb submitted. + +2008-02-18 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Make default size 1024. + * RNBRemote.cpp: Questionmark packet should stay under + max_packet_size - 5 to allow for start, end, checksum and nul + char bytes. + +2008-02-18 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Add m_max_packet_size to class defn. + * RNBRemote.cpp: Initialize it, use it. + +2008-02-18 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Add set_max_packet_size. + * RNBRemote.cpp: Add QSetMaxPacketSize packet handling. + +2008-02-18 Greg Clayton + + * test-remotenub.cpp (HandleProcessStateChange): Call new + RNBRemote::FlushSTDIO function. + (RNBRunLoopInferiorExecuting): Ditto. + * RNBRemote.h (RNBRemote::FlushSTDIO): New prototype. + * RNBRemote.cpp (RNBRemote::FlushSTDIO): New function to + centralize the stdio. + +2008-02-18 Greg Clayton + + * DNB.cpp (DNBProcessWaitForEvent): Added timeout pointer as + parameter that can be NULL for infinite timeout to simplify + the DNB interface. + (DNBProcessTimedWaitForEvent): Removed function. + * DNB.h (DNBProcessWaitForEvent): Added timeout argument. + (DNBProcessTimedWaitForEvent): Removed prototype. + * DNBTimer.h (DNBTimer::OffsetTimeOfDay): New function. + * CFString.cpp (CFString::GetLength() const): New function. + * CFString.h (CFString::GetLength() const): New prototype. + * MachProcess.h (MachProcess class): Removed m_attached and + added m_flags. + * MachProcess.cpp (MachProcess::AttachForDebug): Set m_flags + to indicate we attached. + (MachProcess::SBLaunchForDebug): Set m_flags to indicate we + attached using SpringBoard and that we attached. + (MachProcess::SBForkChildForPTraceDebugging): Changed to new + SpringBoardServices API. + (MachProcess::ThreadFunctionException): Added code that will + renew a watchdog assertion when we launch apps through + SpringBoardServices. + * PThreadEvent.cpp (PThreadEvent::WaitForSetEvents): Simplified + PThreadEvent API to have only one version of WaitForSetEvents + that has an optional timeout pointer argument. + * RNBContext.cpp (RNBContext::StopProcessStatusThread): Adapt + to new PThreadEvent API changes. + (RNBContext::ThreadFunctionProcessStatus): Adapt to new + DNBProcessWaitForEvent API changes. + * RNBRemote.cpp (RNBRemote::StopReadRemoteDataThread): Adapt + to new PThreadEvent API changes. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Adapt to new + DNBProcessWaitForEvent API changes. + (RNBRunLoopInferiorExecuting): Process STDIO first, then + incoming packets. + +2008-02-14 Jason Molenda (jmolenda@apple.com) + + * MachProcess.cpp: (MachProcess::SBForkChildForPTraceDebugging): + Set mode bits on slave side of pty. + +2008-02-12 Greg Clayton + + * DNB.cpp (DNBEnableLogging): Removed function. + (DNBThreadPrintStopReason): Removed the file handle from this + function and use DNBLog calls. + * DNB.h (DNBEnableLogging): Removed function prototype. + (DNBThreadPrintStopReason): Removed the file handle + from the function prototype in favor of using DNBLog calls. + * DNBDataRef.cpp (DNBDataRef::Dump): Removed file handle to use + DNBLog for the logging and print a log line each time a full line + is ready for output after caching it in a local buffer. + * DNBDataRef.cpp (DNBDataRef::Dump): Removed file handle from + prototype. + * DNBDefs.h (DNBCallbackLog): New callback prototype for all + logging. + DNBLog.cpp(g_debug_opt): Renamed to d_debug and made it a file + static. + (DNBLogGetDebug): New accessor function for g_debug. + (DNBLogSetDebug): New accessor function for g_debug. + (g_verbose): Made into a file static and added accessors. + (DNBLogGetVerbose): New accessor function for g_verbose. + (DNBLogSetVerbose): New accessor function for g_verbose. + (DNBLogSetLogCallback): New function call that registers a logging + callback for all logging in libdebugnub.dylib and any code that + loads it. + (DNBLogToASL): Removed function as it is deprecated in favor of + using DNBLogSetLogCallback to regsiter a callback function that + implements the logging. + (DNBLogToFile): Ditto. + (DNBLogCloseLogFile): Ditto. + (DNBLogToFile): Ditto. + (DNBLogToFile): Ditto. + (_DNBLogPuts): Removed unused function. + (_DNBLogVAPrintf): Calls the callback function to do the logging + if one has been registered. + * DNBLog.h (DNBLOG_FLAG_FATAL): New defines that get passed to + any registered logging callback functions. + (DNBLOG_FLAG_FATAL): Ditto. + (DNBLOG_FLAG_ERROR): Ditto. + (DNBLOG_FLAG_WARNING): Ditto. + (DNBLOG_FLAG_DEBUG): Ditto. + (DNBLOG_FLAG_VERBOSE): Ditto. + (DNBLOG_FLAG_THREADED): Ditto. + (DNBLog*): All logging calls are now exported from libdebugnub.dylib + so there aren't two copies (one in debugserver and one in debugnub). + C99 vararg Macros wrap all logging calls so no var arg processing + occurs when logging is disabled. + * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Removed file + handle and now use DNBLog calls. + * DNBRegisterInfo.h (DNBRegisterValueClass::Dump): Removed file + handle from prototype. + * MachException.cpp (catch_mach_exception_raise_state_identity): + Removed newlines from logging call. + (catch_mach_exception_raise): Ditto. + (MachException::Message::Dump): Removed file handle from params + and removed newlines from logging call. + (MachException::ThreadMessage::DumpStopReason): Removed file handle + from params and use DNBLog for logging output. + (MachException::ThreadMessage::Dump): Log using DNBLog instead of + file handle. + * MachProcess.cpp (MachProcess::DumpThreadStoppedReason): Ditto. + (MachProcess::ReadMemory): Ditto. + (MachProcess::WriteMemory): Ditto. + (ExceptionMessageBundleComplete): Ditto. + * MachThread.cpp (MachThread::Dump): Ditto. + (MachThread::DumpRegisterState): Ditto. + * MachThreadList.cpp (MachThreadList::DumpThreadStoppedReason): Ditto. + (MachThreadList::Dump): Ditto. + * RNBRemote.cpp (set_logging): Use new function callback registration + calls when enabling ASL logging. + test-remotenub.cpp (ASLLogCallback): New function to handle all ASL + logging. This function gets registered with libdebugnub.dylib when we + want to log using ASL. + (FileLogCallback): New function to handle all file logging. This + function gets registered with libdebugnub.dylib when we want to log + to a 'FILE *'. + (main): Register the logging callback functions when we want to log + to file or using ASL. + +2008-02-12 Greg Clayton + + * test-remotenub.cpp (main): Default to ASL logging with no log + bits set to allow for warning and error logging. + * RNBRemote.h (struct Breakpoint): New structure for ref counting + breakpoints in Z and z packets. + * RNBRemote.cpp (RNBRemote::SendPacket): Use new LOG_RNB_PACKETS + defined when logging actual packet content. + (RNBRemote::HandleAsyncPacket): Ditto. + (RNBRemote::HandleReceivedPacket): Ditto. + (RNBRemote::HandlePacket_z): Ref count the setting and removing + of breakpoints with the Z and z packets using new struct + RNBRemote::Breakpoint. + * RNBDefs.h (LOG_RNB_PACKETS): New define for logging the sending + and receiving of packets data. + * DNB.cpp (DNBPrintf): Check for NULL file handle. + * DNBBreakpoint.cpp (DNBBreakpoint::Dump): Ditto. + (DNBBreakpointList::Dump): Ditto. + * DNBDefs.h (LOG_EVENTS): New define for logging PThreadEvent. + * DNBLog.cpp (g_debug_opt): Relocated outside of #if that turns off + logging completely to allow option parsing code that uses it to + still compile. + (g_verbose): Ditto. + * DNBLog.h (DNBLogToASL): Added prototype for when logging is + disabled via preprocessor macro. + (DNBLogToFile): Ditto. + * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Check for NULL + file handle. + * MachException.cpp (MachException::ThreadMessage::DumpStopReason): Ditto. + (MachException::ThreadMessage::Dump): Ditto. + * MachProcess.cpp (MachProcess::CreateBreakpoint): Improved logging. + (MachProcess::DisableBreakpoint): Verify the original opcode gets + restored, improved logging and added unconditional logging for when + things go wrong. + (MachProcess::EnableBreakpoint): Verify the breakpoint opcode gets + written, improved logging and added unconditional logging for when + things go wrong. + * MachThread.cpp (MachThread::Dump): Check for NULL file handle. + * MachVMMemory.cpp (MachVMMemory::WriteRegion): Flush caches in inferior + after writing to inferior memory. + * PThreadEvent.cpp: Changed all logging calls to key off of LOG_EVENTS + instead of LOG_VERBOSE. + MachDYLD.cpp (MachDYLD::Dump): Check for NULL file handle. + (MachDYLD::DYLIBInfo::Dump): Ditto. + ProfileObjectiveC.cpp (ProfileObjectiveC::DumpStats): Ditto. + +2008-02-09 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (set_logging): Log to ASL unconditionally when + processing a QSetLogging packet. + +2008-02-06 Greg Clayton + + * test-remotenub.cpp (main): Dup stdout and stderr to /dev/NULL + when we use lockdown. + +2008-02-06 Greg Clayton + + * RNBSocket.cpp (RNBSocket::Disconnect): Removed unused var ERR. + * RNBRemote.cpp(RNBRemote::HandlePacket_Q): Removed unused var PID. + * DNBError.cpp (DNBError::LogThreadedIfError): Removed unused var + ERR_MSG. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Removed unused + variable EXECUTABLE_LENGTH. + (main): Removed unused variable ARG_IDX. + +2008-02-06 Chris Marcellino (cmarcellino@apple.com) and Myke Olson (molson@apple.com) + + * MachProcess.cpp (SBForkChildForPTraceDebugging): Bring up to date with + current SpringBoardServices.framework types and imports. + +2008-02-05 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (set_logging): Remove the mode=file and filename= + options to the QSetLogging packet. We're only going to support logging + to ASL for now. Logging to a file can still be accomplished by the + -l command line argument. + +2008-02-02 Christopher Friesen (cfriesen@apple.com) + + * Added libXcodeDebugerSupport.dylib target + * XCDebuggerIntrospection.[hc]: Support for Xcode's debugger introspection. + +2008-02-01 Jason Molenda (jmolenda@apple.com) + + * DNBLog.cpp (DNBLogCloseLogFile): New function to close a logfile + at exit. + * DNBLog.h: Prototype. + * test-remotenub.cpp (main): Close the log file before exiting. + +2008-02-01 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (set_logging): Recognize the "filename=" argument + to the QSetLogging directive. + * DNBLog.cpp (DNBLogGetLogMask): New fun.c + * DNBLog.h: Prototype. + +2008-01-31 Jason Molenda (jmolenda@apple.com) + + * DNBLog.cpp: Add ASL logging as a run-time selectable option. + (DNBLogToASL, DNBLogToFile): Functions to switch between logging to + a file and logging via ASL. + * DNBLog.h: Prototypes. + * RNBRemote.cpp (set_logging): Recognize the "mode=" field to enable + asl logging. Skip unrecognized keys. + +2008-01-31 Greg Clayton (gclayton@apple.com) + + * DNB.cpp (sigchld_handler): Better logging when we get a + SIGCHILD and we are watching for process related logging events. + * test-remotenub.cpp (RNBRunLoopInferiorExecuting): Only reset + events when we still have event bits set. + +2008-01-29 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Add set_logging_mode. + * RNBRemote.cpp (RNBRemote::CreatePacketTable): Recognize + QSetLogging. + +2008-01-29 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (set_logging): New function to parse the QSetLogging + packet. + (RNBRemote::HandlePacket_Q): Call it. + +2008-01-28 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: Minimal packet size is 1024 in our gdb now. + * RNBRemote.cpp: Add the stop_pc value in big-endian order to the + T response packet to make it a little easier to follow where gdb + is stepping. + +2008-01-28 Greg Clayton + + * RNBContext.h: Removed m_pid_state from RNBContext class so that + it couldn't get out of sync with the actual process and its accessors + SetProcessState() and GetProcessState(). + * RNBContext.cpp (RNBContext::ProcessStateRunning): Always return the + current state of the process instead of a cached value. + * test-remotenub.cpp (RNBRunLoopLaunchInferior): Remove call to + deprecated RNBContext::SetProcessState(). + (HandleProcessStateChange): Ditto. + +2008-01-24 Greg Clayton (gclayton@apple.com) + + * RNBRemote.cpp (RNBRemote::HandlePacket_q): See if command starts with + "qSymbol" (no trailing "s") and return the empty string. + +2008-01-24 Greg Clayton (gclayton@apple.com) + + * RNBRemote.cpp (RNBRemote::HandlePacket_q): See if command starts with + "qSymbols" and return the empty string. + +2008-01-24 Greg Clayton (gclayton@apple.com) + + * DNBError.h (DNBError::DumpIfError): Removed prototype. + * DNBError.cpp (DNBError::DumpIfError): Removed function. + (DNBError::LogThreadedIfError): Output error as hex. + * MachException.cpp (MachException::Message::Receive): Don't use + DNBError::DumpIfError, now use DNBError::LogThreadedIfError. + * MachProcess.cpp (MachProcess::StartExceptionThread): Ditto. + (MachProcess::Suspend): Ditto. + (MachProcess::SBForkChildForPTraceDebugging): Ditto. + * MachVMMemory.cpp (MachVMMemory::Read): Cleaned up logging + calls. + (MachVMMemory::Write): Ditto. + (MachVMMemory::WriteRegion): Added logging. + * RNBContenxt.cpp (display_thread_info): Removed function. + * RNBRemote.cpp (RNBRemote::GetPacket): Commented out stderr + messages to avoid SpringBoard from killing us. + (RNBRemote::HandlePacket_p): Ditto. + (RNBRemote::HandlePacket_P): Ditto. + (RNBRemote::HandlePacket_c): Ditto. + (RNBRemote::HandlePacket_A): Removed code that was already + * RNBSocket.cpp (RNBSocket::Listen): Commented out stdout + messages to avoid SpringBoard from killing us. + (RNBSocket::ConnectToService): Ditto. + +2008-01-24 Jim Ingham + + * RNBRemote.cpp (RNBRemote::HandlePacket_q): Reply "" to qSymbols + and qOffsets. + +2008-01-23 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.h: m_noack_mode to RNBRemote class. + * RNBRemote.cpp: Change #ifdef NO_ACKS code blocks + to use m_noack_mode instance variable. + (RNBRemote::HandlePacket_Q): New function to handle + QStartNoAckMode packet and set m_noack_mode appropriately. + * test-remotenub.cpp: Remove NO_ACKS ifdefs. + +2008-01-22 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (RNBRemote::CreatePacketTable): Recognize + QStartNoAckMode as an unsupported remote protocol request. + * RNBRemote.h: Add start_noack_mode enum entry. + +2008-01-22 Greg Clayton (gclayton@apple.com) + + * DNBLog.h: Removed C++ namespace for DNBLog (changed all DNBLog:: + to DNBLog) so C99 var arg macros can be used to completely disable + all logging and any functions that may be called when making the + variable arguments. + * DNBLog.cpp: Ditto. + * DNB.cpp: Ditto. + * DNBBreakpoint.cpp: Ditto. + * DNBError.cpp: Ditto. + * MacOSX/MachDYLD.cpp: Ditto. + * MacOSX/MachException.cpp: Ditto. + * MacOSX/MachProcess.cpp: Ditto. + * MacOSX/MachThread.cpp: Ditto. + * MacOSX/MachThreadList.cpp: Ditto. + * MacOSX/MachVMMemory.cpp: Ditto. + * MacOSX/MachVMRegion.cpp: Ditto. + * MacOSX/arm/DNBArchImpl.cpp: Ditto. + * MacOSX/ppc/DNBArchImpl.cpp: Ditto. + * PThreadEvent.cpp: Ditto. + * RNBContext.cpp: Ditto. + * RNBRemote.cpp: Ditto. + * RNBSocket.cpp: Ditto. + * test-remotenub.cpp: Ditto. + +2008-01-21 Jason Molenda (jmolenda@apple.com) + + * test-remotenub.cpp: Add NO_SPRINGBOARD for turning off SpringBoard + dependency ala NO_ACKS. + +2008-01-18 Jason Molenda (jmolenda@apple.com) + + * RNBSocket.h (RNBSocket::RNBSocket): Take either a port # or + an already-opened socket, with a boolean to indicate which it is. + * RNBRemote.cpp (RNBRemote::RNBRemote): Ditto. + * RNBRemote.h: Prototype update. + * test-remotenub.cpp: Include lockdown.h. Take --lockdown command + line arg, get the socket from liblockdown.dylib instead of opening + our own socket if it is specified. --lockdown indicates that + the program name/args will be provided via remote protocol instead + of on the command line. + +2008-01-17 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp: Add NO_ACKS #ifdefs around code that computes + the checksums and sends/expects the gdb remote protocol ACK packets. + If NO_ACKS is defined, debugserver will not send or expect acks. + * test-remotenub.cpp (main): Print a different version string + if NO_ACKS is defined. + +2008-01-16 Greg Clayton (gclayton@apple.com) + + * PThreadEvent.cpp: Added this pointer to all logging calls. + +2008-01-16 Greg Clayton (gclayton@apple.com) + + * RNBSocket.cpp (RNBSocket::Connect()): Use TCP so we can try the + TCP_NODELAY socket option. + (RNBSocket::SetSocketOption()): New function. + * RNBSocket.h (RNBSocket::SetSocketOption()): New class function. + +2008-01-14 Jason Molenda (jmolenda@apple.com) + + * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): When printing + registers, skip over gdb regs which don't map to DNB regs. + +2008-01-14 Jim Ingham + + * ChangeLog - created. + * RBNContext.h: Added m_arg_vec and accessors. + * RNBContext.cpp (SetProcessID): New function. + * RBNRemote.h: Added packet type to HandlePacket & HandleAsyncPacket + * RNBRemote.cpp (HandlePacket, HandleAsyncPacket): Return type. + (HandlePacket_A): Fix a few bugs. + (HandlePacket_H): Return OK if target is not yet running. + (HandlePacket_q): Return PID of 0 if target is not yet running. + * testremotenub.cpp (RNBRunLoopGetArgsFromRemote): Implement. + (RNBRunLoopLaunchInferior): Fetch arguments from context. + (main) Store arguments in context, call RNBRunLoopGetArgsFromRemote + if appropriate. diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp new file mode 100644 index 000000000000..72a111bab2cd --- /dev/null +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -0,0 +1,1996 @@ +//===-- DNB.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/23/07. +// +//===----------------------------------------------------------------------===// + +#include "DNB.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachTask.h" +#include "CFString.h" +#include "DNBLog.h" +#include "DNBDataRef.h" +#include "DNBThreadResumeActions.h" +#include "DNBTimer.h" + +typedef std::tr1::shared_ptr MachProcessSP; +typedef std::map ProcessMap; +typedef ProcessMap::iterator ProcessMapIter; +typedef ProcessMap::const_iterator ProcessMapConstIter; + +static size_t GetAllInfos (std::vector& proc_infos); +static size_t GetAllInfosMatchingName (const char *process_name, std::vector& matching_proc_infos); + +//---------------------------------------------------------------------- +// A Thread safe singleton to get a process map pointer. +// +// Returns a pointer to the existing process map, or a pointer to a +// newly created process map if CAN_CREATE is non-zero. +//---------------------------------------------------------------------- +static ProcessMap* +GetProcessMap(bool can_create) +{ + static ProcessMap* g_process_map_ptr = NULL; + + if (can_create && g_process_map_ptr == NULL) + { + static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER; + PTHREAD_MUTEX_LOCKER (locker, &g_process_map_mutex); + if (g_process_map_ptr == NULL) + g_process_map_ptr = new ProcessMap; + } + return g_process_map_ptr; +} + +//---------------------------------------------------------------------- +// Add PID to the shared process pointer map. +// +// Return non-zero value if we succeed in adding the process to the map. +// The only time this should fail is if we run out of memory and can't +// allocate a ProcessMap. +//---------------------------------------------------------------------- +static nub_bool_t +AddProcessToMap (nub_process_t pid, MachProcessSP& procSP) +{ + ProcessMap* process_map = GetProcessMap(true); + if (process_map) + { + process_map->insert(std::make_pair(pid, procSP)); + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// Remove the shared pointer for PID from the process map. +// +// Returns the number of items removed from the process map. +//---------------------------------------------------------------------- +static size_t +RemoveProcessFromMap (nub_process_t pid) +{ + ProcessMap* process_map = GetProcessMap(false); + if (process_map) + { + return process_map->erase(pid); + } + return 0; +} + +//---------------------------------------------------------------------- +// Get the shared pointer for PID from the existing process map. +// +// Returns true if we successfully find a shared pointer to a +// MachProcess object. +//---------------------------------------------------------------------- +static nub_bool_t +GetProcessSP (nub_process_t pid, MachProcessSP& procSP) +{ + ProcessMap* process_map = GetProcessMap(false); + if (process_map != NULL) + { + ProcessMapIter pos = process_map->find(pid); + if (pos != process_map->end()) + { + procSP = pos->second; + return true; + } + } + procSP.reset(); + return false; +} + + +static void * +waitpid_thread (void *arg) +{ + const pid_t pid = (pid_t)(intptr_t)arg; + int status; + while (1) + { + pid_t child_pid = waitpid(pid, &status, 0); + DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): waitpid (pid = %i, &status, 0) => %i, status = %i, errno = %i", pid, child_pid, status, errno); + + if (child_pid < 0) + { + if (errno == EINTR) + continue; + break; + } + else + { + if (WIFSTOPPED(status)) + { + continue; + } + else// if (WIFEXITED(status) || WIFSIGNALED(status)) + { + DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): setting exit status for pid = %i to %i", child_pid, status); + DNBProcessSetExitStatus (child_pid, status); + return NULL; + } + } + } + + // We should never exit as long as our child process is alive, so if we + // do something else went wrong and we should exit... + DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): main loop exited, setting exit status to an invalid value (-1) for pid %i", pid); + DNBProcessSetExitStatus (pid, -1); + return NULL; +} + +static bool +spawn_waitpid_thread (pid_t pid) +{ + pthread_t thread = THREAD_NULL; + ::pthread_create (&thread, NULL, waitpid_thread, (void *)(intptr_t)pid); + if (thread != THREAD_NULL) + { + ::pthread_detach (thread); + return true; + } + return false; +} + +nub_process_t +DNBProcessLaunch (const char *path, + char const *argv[], + const char *envp[], + const char *stdio_path, + nub_launch_flavor_t launch_flavor, + char *err_str, + size_t err_len) +{ + DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, launch_flavor = %u, err = %p, err_len = %zu) called...", __FUNCTION__, path, argv, envp, launch_flavor, err_str, err_len); + + if (err_str && err_len > 0) + err_str[0] = '\0'; + struct stat path_stat; + if (::stat(path, &path_stat) == -1) + { + char stat_error[256]; + ::strerror_r (errno, stat_error, sizeof(stat_error)); + snprintf(err_str, err_len, "%s (%s)", stat_error, path); + return INVALID_NUB_PROCESS; + } + + MachProcessSP processSP (new MachProcess); + if (processSP.get()) + { + DNBError launch_err; + pid_t pid = processSP->LaunchForDebug(path, argv, envp, stdio_path, launch_flavor, launch_err); + if (err_str) + { + *err_str = '\0'; + if (launch_err.Fail()) + { + const char *launch_err_str = launch_err.AsString(); + if (launch_err_str) + { + strncpy(err_str, launch_err_str, err_len-1); + err_str[err_len-1] = '\0'; // Make sure the error string is terminated + } + } + } + + DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid); + + if (pid != INVALID_NUB_PROCESS) + { + // Spawn a thread to reap our child inferior process... + spawn_waitpid_thread (pid); + + if (processSP->Task().TaskPortForProcessID (launch_err) == TASK_NULL) + { + // We failed to get the task for our process ID which is bad. + if (err_str && err_len > 0) + { + if (launch_err.AsString()) + { + ::snprintf (err_str, err_len, "failed to get the task for process %i (%s)", pid, launch_err.AsString()); + } + else + { + ::snprintf (err_str, err_len, "failed to get the task for process %i", pid); + } + } + } + else + { + assert(AddProcessToMap(pid, processSP)); + return pid; + } + } + } + return INVALID_NUB_PROCESS; +} + +nub_process_t +DNBProcessAttachByName (const char *name, struct timespec *timeout, char *err_str, size_t err_len) +{ + if (err_str && err_len > 0) + err_str[0] = '\0'; + std::vector matching_proc_infos; + size_t num_matching_proc_infos = GetAllInfosMatchingName(name, matching_proc_infos); + if (num_matching_proc_infos == 0) + { + DNBLogError ("error: no processes match '%s'\n", name); + return INVALID_NUB_PROCESS; + } + else if (num_matching_proc_infos > 1) + { + DNBLogError ("error: %u processes match '%s':\n", num_matching_proc_infos, name); + size_t i; + for (i=0; i 0) + err_str[0] = '\0'; + + pid_t pid; + MachProcessSP processSP(new MachProcess); + if (processSP.get()) + { + DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...", attach_pid); + pid = processSP->AttachForDebug (attach_pid, err_str, err_len); + + if (pid != INVALID_NUB_PROCESS) + { + assert(AddProcessToMap(pid, processSP)); + spawn_waitpid_thread(pid); + } + } + + while (pid != INVALID_NUB_PROCESS) + { + // Wait for process to start up and hit entry point + DNBLogThreadedIf (LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged " + "| eEventProcessStoppedStateChanged, true, INFINITE)...", + __FUNCTION__, pid); + nub_event_t set_events = DNBProcessWaitForEvents (pid, + eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, + true, timeout); + DNBLogThreadedIf (LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged " + "| eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", + __FUNCTION__, pid, set_events); + + if (set_events == 0) + { + if (err_str && err_len > 0) + snprintf(err_str, err_len, "operation timed out"); + pid = INVALID_NUB_PROCESS; + } + else + { + if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged)) + { + nub_state_t pid_state = DNBProcessGetState (pid); + DNBLogThreadedIf (LOG_PROCESS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", + __FUNCTION__, pid, DNBStateAsString(pid_state)); + + switch (pid_state) + { + default: + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateSuspended: + break; // Ignore + + case eStateRunning: + case eStateStepping: + // Still waiting to stop at entry point... + break; + + case eStateStopped: + case eStateCrashed: + return pid; + case eStateDetached: + case eStateExited: + if (err_str && err_len > 0) + snprintf(err_str, err_len, "process exited"); + return INVALID_NUB_PROCESS; + } + } + + DNBProcessResetEvents(pid, set_events); + } + } + + return INVALID_NUB_PROCESS; +} + +static size_t +GetAllInfos(std::vector& proc_infos) +{ + size_t size; + int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + u_int namelen = sizeof(name)/sizeof(int); + int err; + + // Try to find out how many processes are around so we can + // size the buffer appropriately. sysctl's man page specifically suggests + // this approach, and says it returns a bit larger size than needed to + // handle any new processes created between then and now. + + err = ::sysctl (name, namelen, NULL, &size, NULL, 0); + + if ((err < 0) && (err != ENOMEM)) + { + proc_infos.clear(); + perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)"); + return 0; + } + + + // Increase the size of the buffer by a few processes in case more have + // been spawned + proc_infos.resize (size / sizeof(struct kinfo_proc)); + size = proc_infos.size() * sizeof(struct kinfo_proc); // Make sure we don't exceed our resize... + err = ::sysctl (name, namelen, &proc_infos[0], &size, NULL, 0); + if (err < 0) + { + proc_infos.clear(); + return 0; + } + + // Trim down our array to fit what we actually got back + proc_infos.resize(size / sizeof(struct kinfo_proc)); + return proc_infos.size(); +} + + +static size_t +GetAllInfosMatchingName(const char *full_process_name, std::vector& matching_proc_infos) +{ + + matching_proc_infos.clear(); + if (full_process_name && full_process_name[0]) + { + // We only get the process name, not the full path, from the proc_info. So just take the + // base name of the process name... + const char *process_name; + process_name = strrchr (full_process_name, '/'); + if (process_name == NULL) + process_name = full_process_name; + else + process_name++; + + std::vector proc_infos; + const size_t num_proc_infos = GetAllInfos(proc_infos); + if (num_proc_infos > 0) + { + uint32_t i; + for (i=0; i exclude_proc_infos; + size_t num_exclude_proc_infos; + + // If the PrepareForAttach returns a valid token, use MachProcess to check + // for the process, otherwise scan the process table. + + const void *attach_token = MachProcess::PrepareForAttach (waitfor_process_name, launch_flavor, true, prepare_error); + + if (prepare_error.Fail()) + { + DNBLogError ("Error in PrepareForAttach: %s", prepare_error.AsString()); + return INVALID_NUB_PROCESS; + } + + if (attach_token == NULL) + num_exclude_proc_infos = GetAllInfosMatchingName (waitfor_process_name, exclude_proc_infos); + + DNBLogThreadedIf (LOG_PROCESS, "Waiting for '%s' to appear...\n", waitfor_process_name); + + // Loop and try to find the process by name + nub_process_t waitfor_pid = INVALID_NUB_PROCESS; + + while (waitfor_pid == INVALID_NUB_PROCESS) + { + if (attach_token != NULL) + { + nub_process_t pid; + pid = MachProcess::CheckForProcess(attach_token); + if (pid != INVALID_NUB_PROCESS) + { + waitfor_pid = pid; + break; + } + } + else + { + + // Get the current process list, and check for matches that + // aren't in our original list. If anyone wants to attach + // to an existing process by name, they should do it with + // --attach=PROCNAME. Else we will wait for the first matching + // process that wasn't in our exclusion list. + std::vector proc_infos; + const size_t num_proc_infos = GetAllInfosMatchingName (waitfor_process_name, proc_infos); + for (size_t i=0; i 0) + snprintf(err_str, err_len, "operation timed out"); + DNBLogError ("error: waiting for process '%s' timed out.\n", waitfor_process_name); + return INVALID_NUB_PROCESS; + } + } + + // Call the should cancel callback as well... + + if (should_cancel_callback != NULL + && should_cancel_callback (callback_data)) + { + DNBLogThreadedIf (LOG_PROCESS, "DNBProcessAttachWait cancelled by should_cancel callback."); + waitfor_pid = INVALID_NUB_PROCESS; + break; + } + + ::usleep (waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again + } + } + + if (waitfor_pid != INVALID_NUB_PROCESS) + { + DNBLogThreadedIf (LOG_PROCESS, "Attaching to %s with pid %i...\n", waitfor_process_name, waitfor_pid); + waitfor_pid = DNBProcessAttach (waitfor_pid, timeout_abstime, err_str, err_len); + } + + bool success = waitfor_pid != INVALID_NUB_PROCESS; + MachProcess::CleanupAfterAttach (attach_token, success, prepare_error); + + return waitfor_pid; +} + +nub_bool_t +DNBProcessDetach (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->Detach(); + } + return false; +} + +nub_bool_t +DNBProcessKill (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->Kill (); + } + return false; +} + +nub_bool_t +DNBProcessSignal (nub_process_t pid, int signal) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->Signal (signal); + } + return false; +} + + +nub_bool_t +DNBProcessIsAlive (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return MachTask::IsValid (procSP->Task().TaskPort()); + } + return eStateInvalid; +} + +//---------------------------------------------------------------------- +// Process and Thread state information +//---------------------------------------------------------------------- +nub_state_t +DNBProcessGetState (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetState(); + } + return eStateInvalid; +} + +//---------------------------------------------------------------------- +// Process and Thread state information +//---------------------------------------------------------------------- +nub_bool_t +DNBProcessGetExitStatus (nub_process_t pid, int* status) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->GetExitStatus(status); + } + return false; +} + +nub_bool_t +DNBProcessSetExitStatus (nub_process_t pid, int status) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + procSP->SetExitStatus(status); + return true; + } + return false; +} + + +const char * +DNBThreadGetName (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->ThreadGetName(tid); + return NULL; +} + + +nub_bool_t +DNBThreadGetIdentifierInfo (nub_process_t pid, nub_thread_t tid, thread_identifier_info_data_t *ident_info) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info); + return false; +} + +nub_state_t +DNBThreadGetState (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->ThreadGetState(tid); + } + return eStateInvalid; +} + +const char * +DNBStateAsString(nub_state_t state) +{ + switch (state) + { + case eStateUnloaded: return "Unloaded"; + case eStateAttaching: return "Attaching"; + case eStateLaunching: return "Launching"; + case eStateStopped: return "Stopped"; + case eStateRunning: return "Running"; + case eStateStepping: return "Stepping"; + case eStateCrashed: return "Crashed"; + case eStateDetached: return "Detached"; + case eStateExited: return "Exited"; + case eStateSuspended: return "Suspended"; + } + return "nub_state_t ???"; +} + +const char * +DNBProcessGetExecutablePath (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->Path(); + } + return NULL; +} + +nub_size_t +DNBProcessGetArgumentCount (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->ArgumentCount(); + } + return 0; +} + +const char * +DNBProcessGetArgumentAtIndex (nub_process_t pid, nub_size_t idx) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->ArgumentAtIndex (idx); + } + return NULL; +} + + +//---------------------------------------------------------------------- +// Execution control +//---------------------------------------------------------------------- +nub_bool_t +DNBProcessResume (nub_process_t pid, const DNBThreadResumeAction *actions, size_t num_actions) +{ + DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid); + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBThreadResumeActions thread_actions (actions, num_actions); + + // Below we add a default thread plan just in case one wasn't + // provided so all threads always know what they were supposed to do + if (thread_actions.IsEmpty()) + { + // No thread plans were given, so the default it to run all threads + thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0); + } + else + { + // Some thread plans were given which means anything that wasn't + // specified should remain stopped. + thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0); + } + return procSP->Resume (thread_actions); + } + return false; +} + +nub_bool_t +DNBProcessHalt (nub_process_t pid) +{ + DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid); + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->Signal (SIGSTOP); + return false; +} +// +//nub_bool_t +//DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step) +//{ +// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)", __FUNCTION__, pid, tid, (uint32_t)step); +// MachProcessSP procSP; +// if (GetProcessSP (pid, procSP)) +// { +// return procSP->Resume(tid, step, 0); +// } +// return false; +//} +// +//nub_bool_t +//DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t step, int signal) +//{ +// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u, signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal); +// MachProcessSP procSP; +// if (GetProcessSP (pid, procSP)) +// { +// return procSP->Resume(tid, step, signal); +// } +// return false; +//} + +nub_event_t +DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout) +{ + nub_event_t result = 0; + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + if (wait_for_set) + result = procSP->Events().WaitForSetEvents(event_mask, timeout); + else + result = procSP->Events().WaitForEventsToReset(event_mask, timeout); + } + return result; +} + +void +DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + procSP->Events().ResetEvents(event_mask); +} + +void +DNBProcessInterruptEvents (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + procSP->Events().SetEvents(eEventProcessAsyncInterrupt); +} + + +// Breakpoints +nub_break_t +DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->CreateBreakpoint(addr, size, hardware, THREAD_NULL); + } + return INVALID_NUB_BREAK_ID; +} + +nub_bool_t +DNBBreakpointClear (nub_process_t pid, nub_break_t breakID) +{ + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->DisableBreakpoint(breakID, true); + } + } + return false; // Failed +} + +nub_ssize_t +DNBBreakpointGetHitCount (nub_process_t pid, nub_break_t breakID) +{ + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID); + if (bp) + return bp->GetHitCount(); + } + } + return 0; +} + +nub_ssize_t +DNBBreakpointGetIgnoreCount (nub_process_t pid, nub_break_t breakID) +{ + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID); + if (bp) + return bp->GetIgnoreCount(); + } + } + return 0; +} + +nub_bool_t +DNBBreakpointSetIgnoreCount (nub_process_t pid, nub_break_t breakID, nub_size_t ignore_count) +{ + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID); + if (bp) + { + bp->SetIgnoreCount(ignore_count); + return true; + } + } + } + return false; +} + +// Set the callback function for a given breakpoint. The callback function will +// get called as soon as the breakpoint is hit. The function will be called +// with the process ID, thread ID, breakpoint ID and the baton, and can return +// +nub_bool_t +DNBBreakpointSetCallback (nub_process_t pid, nub_break_t breakID, DNBCallbackBreakpointHit callback, void *baton) +{ + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID); + if (bp) + { + bp->SetCallback(callback, baton); + return true; + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// Dump the breakpoints stats for process PID for a breakpoint by ID. +//---------------------------------------------------------------------- +void +DNBBreakpointPrint (nub_process_t pid, nub_break_t breakID) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + procSP->DumpBreakpoint(breakID); +} + +//---------------------------------------------------------------------- +// Watchpoints +//---------------------------------------------------------------------- +nub_watch_t +DNBWatchpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, uint32_t watch_flags, nub_bool_t hardware) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->CreateWatchpoint(addr, size, watch_flags, hardware, THREAD_NULL); + } + return INVALID_NUB_BREAK_ID; +} + +nub_bool_t +DNBWatchpointClear (nub_process_t pid, nub_watch_t watchID) +{ + if (NUB_BREAK_ID_IS_VALID(watchID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->DisableWatchpoint(watchID, true); + } + } + return false; // Failed +} + +nub_ssize_t +DNBWatchpointGetHitCount (nub_process_t pid, nub_watch_t watchID) +{ + if (NUB_BREAK_ID_IS_VALID(watchID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID); + if (bp) + return bp->GetHitCount(); + } + } + return 0; +} + +nub_ssize_t +DNBWatchpointGetIgnoreCount (nub_process_t pid, nub_watch_t watchID) +{ + if (NUB_BREAK_ID_IS_VALID(watchID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID); + if (bp) + return bp->GetIgnoreCount(); + } + } + return 0; +} + +nub_bool_t +DNBWatchpointSetIgnoreCount (nub_process_t pid, nub_watch_t watchID, nub_size_t ignore_count) +{ + if (NUB_BREAK_ID_IS_VALID(watchID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID); + if (bp) + { + bp->SetIgnoreCount(ignore_count); + return true; + } + } + } + return false; +} + +// Set the callback function for a given watchpoint. The callback function will +// get called as soon as the watchpoint is hit. The function will be called +// with the process ID, thread ID, watchpoint ID and the baton, and can return +// +nub_bool_t +DNBWatchpointSetCallback (nub_process_t pid, nub_watch_t watchID, DNBCallbackBreakpointHit callback, void *baton) +{ + if (NUB_BREAK_ID_IS_VALID(watchID)) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID); + if (bp) + { + bp->SetCallback(callback, baton); + return true; + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// Dump the watchpoints stats for process PID for a watchpoint by ID. +//---------------------------------------------------------------------- +void +DNBWatchpointPrint (nub_process_t pid, nub_watch_t watchID) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + procSP->DumpWatchpoint(watchID); +} + +//---------------------------------------------------------------------- +// Read memory in the address space of process PID. This call will take +// care of setting and restoring permissions and breaking up the memory +// read into multiple chunks as required. +// +// RETURNS: number of bytes actually read +//---------------------------------------------------------------------- +nub_size_t +DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->ReadMemory(addr, size, buf); + return 0; +} + +//---------------------------------------------------------------------- +// Write memory to the address space of process PID. This call will take +// care of setting and restoring permissions and breaking up the memory +// write into multiple chunks as required. +// +// RETURNS: number of bytes actually written +//---------------------------------------------------------------------- +nub_size_t +DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub_size_t size, const void *buf) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->WriteMemory(addr, size, buf); + return 0; +} + +nub_addr_t +DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->Task().AllocateMemory (size, permissions); + return 0; +} + +nub_bool_t +DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->Task().DeallocateMemory (addr); + return 0; +} + + +//---------------------------------------------------------------------- +// Formatted output that uses memory and registers from process and +// thread in place of arguments. +//---------------------------------------------------------------------- +nub_size_t +DNBPrintf (nub_process_t pid, nub_thread_t tid, nub_addr_t base_addr, FILE *file, const char *format) +{ + if (file == NULL) + return 0; + enum printf_flags + { + alternate_form = (1 << 0), + zero_padding = (1 << 1), + negative_field_width = (1 << 2), + blank_space = (1 << 3), + show_sign = (1 << 4), + show_thousands_separator= (1 << 5), + }; + + enum printf_length_modifiers + { + length_mod_h = (1 << 0), + length_mod_hh = (1 << 1), + length_mod_l = (1 << 2), + length_mod_ll = (1 << 3), + length_mod_L = (1 << 4), + length_mod_j = (1 << 5), + length_mod_t = (1 << 6), + length_mod_z = (1 << 7), + length_mod_q = (1 << 8), + }; + + nub_addr_t addr = base_addr; + char *end_format = (char*)format + strlen(format); + char *end = NULL; // For strtoXXXX calls; + std::basic_string buf; + nub_size_t total_bytes_read = 0; + DNBDataRef data; + const char *f; + for (f = format; *f != '\0' && f < end_format; f++) + { + char ch = *f; + switch (ch) + { + case '%': + { + f++; // Skip the '%' character + int min_field_width = 0; + int precision = 0; + uint32_t flags = 0; + uint32_t length_modifiers = 0; + uint32_t byte_size = 0; + uint32_t actual_byte_size = 0; + bool is_string = false; + bool is_register = false; + DNBRegisterValue register_value; + int64_t register_offset = 0; + nub_addr_t register_addr = INVALID_NUB_ADDRESS; + + // Create the format string to use for this conversion specification + // so we can remove and mprintf specific flags and formatters. + std::string fprintf_format("%"); + + // Decode any flags + switch (*f) + { + case '#': fprintf_format += *f++; flags |= alternate_form; break; + case '0': fprintf_format += *f++; flags |= zero_padding; break; + case '-': fprintf_format += *f++; flags |= negative_field_width; break; + case ' ': fprintf_format += *f++; flags |= blank_space; break; + case '+': fprintf_format += *f++; flags |= show_sign; break; + case ',': fprintf_format += *f++; flags |= show_thousands_separator;break; + case '{': + case '[': + { + // We have a register name specification that can take two forms: + // ${regname} or ${regname+offset} + // The action is to read the register value and add the signed offset + // (if any) and use that as the value to format. + // $[regname] or $[regname+offset] + // The action is to read the register value and add the signed offset + // (if any) and use the result as an address to dereference. The size + // of what is dereferenced is specified by the actual byte size that + // follows the minimum field width and precision (see comments below). + switch (*f) + { + case '{': + case '[': + { + char open_scope_ch = *f; + f++; + const char *reg_name = f; + size_t reg_name_length = strcspn(f, "+-}]"); + if (reg_name_length > 0) + { + std::string register_name(reg_name, reg_name_length); + f += reg_name_length; + register_offset = strtoll(f, &end, 0); + if (f < end) + f = end; + if ((open_scope_ch == '{' && *f != '}') || (open_scope_ch == '[' && *f != ']')) + { + fprintf(file, "error: Invalid register format string. Valid formats are %%{regname} or %%{regname+offset}, %%[regname] or %%[regname+offset]\n"); + return total_bytes_read; + } + else + { + f++; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, register_name.c_str(), ®ister_value)) + { + // Set the address to dereference using the register value plus the offset + switch (register_value.info.size) + { + default: + case 0: + fprintf (file, "error: unsupported register size of %u.\n", register_value.info.size); + return total_bytes_read; + + case 1: register_addr = register_value.value.uint8 + register_offset; break; + case 2: register_addr = register_value.value.uint16 + register_offset; break; + case 4: register_addr = register_value.value.uint32 + register_offset; break; + case 8: register_addr = register_value.value.uint64 + register_offset; break; + case 16: + if (open_scope_ch == '[') + { + fprintf (file, "error: register size (%u) too large for address.\n", register_value.info.size); + return total_bytes_read; + } + break; + } + + if (open_scope_ch == '{') + { + byte_size = register_value.info.size; + is_register = true; // value is in a register + + } + else + { + addr = register_addr; // Use register value and offset as the address + } + } + else + { + fprintf(file, "error: unable to read register '%s' for process %#.4x and thread %#.4x\n", register_name.c_str(), pid, tid); + return total_bytes_read; + } + } + } + } + break; + + default: + fprintf(file, "error: %%$ must be followed by (regname + n) or [regname + n]\n"); + return total_bytes_read; + } + } + break; + } + + // Check for a minimum field width + if (isdigit(*f)) + { + min_field_width = strtoul(f, &end, 10); + if (end > f) + { + fprintf_format.append(f, end - f); + f = end; + } + } + + + // Check for a precision + if (*f == '.') + { + f++; + if (isdigit(*f)) + { + fprintf_format += '.'; + precision = strtoul(f, &end, 10); + if (end > f) + { + fprintf_format.append(f, end - f); + f = end; + } + } + } + + + // mprintf specific: read the optional actual byte size (abs) + // after the standard minimum field width (mfw) and precision (prec). + // Standard printf calls you can have "mfw.prec" or ".prec", but + // mprintf can have "mfw.prec.abs", ".prec.abs" or "..abs". This is nice + // for strings that may be in a fixed size buffer, but may not use all bytes + // in that buffer for printable characters. + if (*f == '.') + { + f++; + actual_byte_size = strtoul(f, &end, 10); + if (end > f) + { + byte_size = actual_byte_size; + f = end; + } + } + + // Decode the length modifiers + switch (*f) + { + case 'h': // h and hh length modifiers + fprintf_format += *f++; + length_modifiers |= length_mod_h; + if (*f == 'h') + { + fprintf_format += *f++; + length_modifiers |= length_mod_hh; + } + break; + + case 'l': // l and ll length modifiers + fprintf_format += *f++; + length_modifiers |= length_mod_l; + if (*f == 'h') + { + fprintf_format += *f++; + length_modifiers |= length_mod_ll; + } + break; + + case 'L': fprintf_format += *f++; length_modifiers |= length_mod_L; break; + case 'j': fprintf_format += *f++; length_modifiers |= length_mod_j; break; + case 't': fprintf_format += *f++; length_modifiers |= length_mod_t; break; + case 'z': fprintf_format += *f++; length_modifiers |= length_mod_z; break; + case 'q': fprintf_format += *f++; length_modifiers |= length_mod_q; break; + } + + // Decode the conversion specifier + switch (*f) + { + case '_': + // mprintf specific format items + { + ++f; // Skip the '_' character + switch (*f) + { + case 'a': // Print the current address + ++f; + fprintf_format += "ll"; + fprintf_format += *f; // actual format to show address with folows the 'a' ("%_ax") + fprintf (file, fprintf_format.c_str(), addr); + break; + case 'o': // offset from base address + ++f; + fprintf_format += "ll"; + fprintf_format += *f; // actual format to show address with folows the 'a' ("%_ox") + fprintf(file, fprintf_format.c_str(), addr - base_addr); + break; + default: + fprintf (file, "error: unsupported mprintf specific format character '%c'.\n", *f); + break; + } + continue; + } + break; + + case 'D': + case 'O': + case 'U': + fprintf_format += *f; + if (byte_size == 0) + byte_size = sizeof(long int); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + fprintf_format += *f; + if (byte_size == 0) + { + if (length_modifiers & length_mod_hh) + byte_size = sizeof(char); + else if (length_modifiers & length_mod_h) + byte_size = sizeof(short); + if (length_modifiers & length_mod_ll) + byte_size = sizeof(long long); + else if (length_modifiers & length_mod_l) + byte_size = sizeof(long); + else + byte_size = sizeof(int); + } + break; + + case 'a': + case 'A': + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + fprintf_format += *f; + if (byte_size == 0) + { + if (length_modifiers & length_mod_L) + byte_size = sizeof(long double); + else + byte_size = sizeof(double); + } + break; + + case 'c': + if ((length_modifiers & length_mod_l) == 0) + { + fprintf_format += *f; + if (byte_size == 0) + byte_size = sizeof(char); + break; + } + // Fall through to 'C' modifier below... + + case 'C': + fprintf_format += *f; + if (byte_size == 0) + byte_size = sizeof(wchar_t); + break; + + case 's': + fprintf_format += *f; + if (is_register || byte_size == 0) + is_string = 1; + break; + + case 'p': + fprintf_format += *f; + if (byte_size == 0) + byte_size = sizeof(void*); + break; + } + + if (is_string) + { + std::string mem_string; + const size_t string_buf_len = 4; + char string_buf[string_buf_len+1]; + char *string_buf_end = string_buf + string_buf_len; + string_buf[string_buf_len] = '\0'; + nub_size_t bytes_read; + nub_addr_t str_addr = is_register ? register_addr : addr; + while ((bytes_read = DNBProcessMemoryRead(pid, str_addr, string_buf_len, &string_buf[0])) > 0) + { + // Did we get a NULL termination character yet? + if (strchr(string_buf, '\0') == string_buf_end) + { + // no NULL terminator yet, append as a std::string + mem_string.append(string_buf, string_buf_len); + str_addr += string_buf_len; + } + else + { + // yep + break; + } + } + // Append as a C-string so we don't get the extra NULL + // characters in the temp buffer (since it was resized) + mem_string += string_buf; + size_t mem_string_len = mem_string.size() + 1; + fprintf(file, fprintf_format.c_str(), mem_string.c_str()); + if (mem_string_len > 0) + { + if (!is_register) + { + addr += mem_string_len; + total_bytes_read += mem_string_len; + } + } + else + return total_bytes_read; + } + else + if (byte_size > 0) + { + buf.resize(byte_size); + nub_size_t bytes_read = 0; + if (is_register) + bytes_read = register_value.info.size; + else + bytes_read = DNBProcessMemoryRead(pid, addr, buf.size(), &buf[0]); + if (bytes_read > 0) + { + if (!is_register) + total_bytes_read += bytes_read; + + if (bytes_read == byte_size) + { + switch (*f) + { + case 'd': + case 'i': + case 'o': + case 'u': + case 'X': + case 'x': + case 'a': + case 'A': + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'p': + case 'c': + case 'C': + { + if (is_register) + data.SetData(®ister_value.value.v_uint8[0], register_value.info.size); + else + data.SetData(&buf[0], bytes_read); + DNBDataRef::offset_t data_offset = 0; + if (byte_size <= 4) + { + uint32_t u32 = data.GetMax32(&data_offset, byte_size); + // Show the actual byte width when displaying hex + fprintf(file, fprintf_format.c_str(), u32); + } + else if (byte_size <= 8) + { + uint64_t u64 = data.GetMax64(&data_offset, byte_size); + // Show the actual byte width when displaying hex + fprintf(file, fprintf_format.c_str(), u64); + } + else + { + fprintf(file, "error: integer size not supported, must be 8 bytes or less (%u bytes).\n", byte_size); + } + if (!is_register) + addr += byte_size; + } + break; + + case 's': + fprintf(file, fprintf_format.c_str(), buf.c_str()); + addr += byte_size; + break; + + default: + fprintf(file, "error: unsupported conversion specifier '%c'.\n", *f); + break; + } + } + } + } + else + return total_bytes_read; + } + break; + + case '\\': + { + f++; + switch (*f) + { + case 'e': ch = '\e'; break; + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case '\'': ch = '\''; break; + case '\\': ch = '\\'; break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + ch = strtoul(f, &end, 8); + f = end; + break; + default: + ch = *f; + break; + } + fputc(ch, file); + } + break; + + default: + fputc(ch, file); + break; + } + } + return total_bytes_read; +} + + +//---------------------------------------------------------------------- +// Get the number of threads for the specified process. +//---------------------------------------------------------------------- +nub_size_t +DNBProcessGetNumThreads (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetNumThreads(); + return 0; +} + +//---------------------------------------------------------------------- +// Get the thread ID of the current thread. +//---------------------------------------------------------------------- +nub_thread_t +DNBProcessGetCurrentThread (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetCurrentThread(); + return 0; +} + +//---------------------------------------------------------------------- +// Change the current thread. +//---------------------------------------------------------------------- +nub_thread_t +DNBProcessSetCurrentThread (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->SetCurrentThread (tid); + return INVALID_NUB_THREAD; +} + + +//---------------------------------------------------------------------- +// Dump a string describing a thread's stop reason to the specified file +// handle +//---------------------------------------------------------------------- +nub_bool_t +DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, struct DNBThreadStopInfo *stop_info) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetThreadStoppedReason (tid, stop_info); + return false; +} + +//---------------------------------------------------------------------- +// Return string description for the specified thread. +// +// RETURNS: NULL if the thread isn't valid, else a NULL terminated C +// string from a static buffer that must be copied prior to subsequent +// calls. +//---------------------------------------------------------------------- +const char * +DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetThreadInfo (tid); + return NULL; +} + +//---------------------------------------------------------------------- +// Get the thread ID given a thread index. +//---------------------------------------------------------------------- +nub_thread_t +DNBProcessGetThreadAtIndex (nub_process_t pid, size_t thread_idx) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetThreadAtIndex (thread_idx); + return INVALID_NUB_THREAD; +} + +nub_addr_t +DNBProcessGetSharedLibraryInfoAddress (nub_process_t pid) +{ + MachProcessSP procSP; + DNBError err; + if (GetProcessSP (pid, procSP)) + return procSP->Task().GetDYLDAllImageInfosAddress (err); + return INVALID_NUB_ADDRESS; +} + + +nub_bool_t +DNBProcessSharedLibrariesUpdated(nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + procSP->SharedLibrariesUpdated (); + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// Get the current shared library information for a process. Only return +// the shared libraries that have changed since the last shared library +// state changed event if only_changed is non-zero. +//---------------------------------------------------------------------- +nub_size_t +DNBProcessGetSharedLibraryInfo (nub_process_t pid, nub_bool_t only_changed, struct DNBExecutableImageInfo **image_infos) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->CopyImageInfos (image_infos, only_changed); + + // If we have no process, then return NULL for the shared library info + // and zero for shared library count + *image_infos = NULL; + return 0; +} + +//---------------------------------------------------------------------- +// Get the register set information for a specific thread. +//---------------------------------------------------------------------- +const DNBRegisterSetInfo * +DNBGetRegisterSetInfo (nub_size_t *num_reg_sets) +{ + return DNBArch::GetRegisterSetInfo (num_reg_sets); +} + + +//---------------------------------------------------------------------- +// Read a register value by register set and register index. +//---------------------------------------------------------------------- +nub_bool_t +DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value) +{ + MachProcessSP procSP; + ::bzero (value, sizeof(DNBRegisterValue)); + if (GetProcessSP (pid, procSP)) + { + if (tid != INVALID_NUB_THREAD) + return procSP->GetRegisterValue (tid, set, reg, value); + } + return false; +} + +nub_bool_t +DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value) +{ + if (tid != INVALID_NUB_THREAD) + { + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->SetRegisterValue (tid, set, reg, value); + } + return false; +} + +nub_size_t +DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + if (tid != INVALID_NUB_THREAD) + return procSP->GetThreadList().GetRegisterContext (tid, buf, buf_len); + } + ::bzero (buf, buf_len); + return 0; + +} + +nub_size_t +DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + if (tid != INVALID_NUB_THREAD) + return procSP->GetThreadList().SetRegisterContext (tid, buf, buf_len); + } + return 0; +} + +//---------------------------------------------------------------------- +// Read a register value by name. +//---------------------------------------------------------------------- +nub_bool_t +DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t reg_set, const char *reg_name, DNBRegisterValue *value) +{ + MachProcessSP procSP; + ::bzero (value, sizeof(DNBRegisterValue)); + if (GetProcessSP (pid, procSP)) + { + const struct DNBRegisterSetInfo *set_info; + nub_size_t num_reg_sets = 0; + set_info = DNBGetRegisterSetInfo (&num_reg_sets); + if (set_info) + { + uint32_t set = reg_set; + uint32_t reg; + if (set == REGISTER_SET_ALL) + { + for (set = 1; set < num_reg_sets; ++set) + { + for (reg = 0; reg < set_info[set].num_registers; ++reg) + { + if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) + return procSP->GetRegisterValue (tid, set, reg, value); + } + } + } + else + { + for (reg = 0; reg < set_info[set].num_registers; ++reg) + { + if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) + return procSP->GetRegisterValue (tid, set, reg, value); + } + } + } + } + return false; +} + + +//---------------------------------------------------------------------- +// Read a register set and register number from the register name. +//---------------------------------------------------------------------- +nub_bool_t +DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info) +{ + const struct DNBRegisterSetInfo *set_info; + nub_size_t num_reg_sets = 0; + set_info = DNBGetRegisterSetInfo (&num_reg_sets); + if (set_info) + { + uint32_t set, reg; + for (set = 1; set < num_reg_sets; ++set) + { + for (reg = 0; reg < set_info[set].num_registers; ++reg) + { + if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) + { + *info = set_info[set].registers[reg]; + return true; + } + } + } + + for (set = 1; set < num_reg_sets; ++set) + { + uint32_t reg; + for (reg = 0; reg < set_info[set].num_registers; ++reg) + { + if (set_info[set].registers[reg].alt == NULL) + continue; + + if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) + { + *info = set_info[set].registers[reg]; + return true; + } + } + } + } + + ::bzero (info, sizeof(DNBRegisterInfo)); + return false; +} + + +//---------------------------------------------------------------------- +// Set the name to address callback function that this nub can use +// for any name to address lookups that are needed. +//---------------------------------------------------------------------- +nub_bool_t +DNBProcessSetNameToAddressCallback (nub_process_t pid, DNBCallbackNameToAddress callback, void *baton) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + procSP->SetNameToAddressCallback (callback, baton); + return true; + } + return false; +} + + +//---------------------------------------------------------------------- +// Set the name to address callback function that this nub can use +// for any name to address lookups that are needed. +//---------------------------------------------------------------------- +nub_bool_t +DNBProcessSetSharedLibraryInfoCallback (nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback, void *baton) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + procSP->SetSharedLibraryInfoCallback (callback, baton); + return true; + } + return false; +} + +nub_addr_t +DNBProcessLookupAddress (nub_process_t pid, const char *name, const char *shlib) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + { + return procSP->LookupSymbol (name, shlib); + } + return INVALID_NUB_ADDRESS; +} + + +nub_size_t +DNBProcessGetAvailableSTDOUT (nub_process_t pid, char *buf, nub_size_t buf_size) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetAvailableSTDOUT (buf, buf_size); + return 0; +} + +nub_size_t +DNBProcessGetAvailableSTDERR (nub_process_t pid, char *buf, nub_size_t buf_size) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->GetAvailableSTDERR (buf, buf_size); + return 0; +} + +nub_size_t +DNBProcessGetStopCount (nub_process_t pid) +{ + MachProcessSP procSP; + if (GetProcessSP (pid, procSP)) + return procSP->StopCount(); + return 0; +} + +nub_bool_t +DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size) +{ + if (path == NULL || path[0] == '\0') + return false; + + char max_path[PATH_MAX]; + std::string result; + CFString::GlobPath(path, result); + + if (result.empty()) + result = path; + + if (realpath(path, max_path)) + { + // Found the path relatively... + ::strncpy(resolved_path, max_path, resolved_path_size); + return strlen(resolved_path) + 1 < resolved_path_size; + } + else + { + // Not a relative path, check the PATH environment variable if the + const char *PATH = getenv("PATH"); + if (PATH) + { + const char *curr_path_start = PATH; + const char *curr_path_end; + while (curr_path_start && *curr_path_start) + { + curr_path_end = strchr(curr_path_start, ':'); + if (curr_path_end == NULL) + { + result.assign(curr_path_start); + curr_path_start = NULL; + } + else if (curr_path_end > curr_path_start) + { + size_t len = curr_path_end - curr_path_start; + result.assign(curr_path_start, len); + curr_path_start += len + 1; + } + else + break; + + result += '/'; + result += path; + struct stat s; + if (stat(result.c_str(), &s) == 0) + { + ::strncpy(resolved_path, result.c_str(), resolved_path_size); + return result.size() + 1 < resolved_path_size; + } + } + } + } + return false; +} + diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h new file mode 100644 index 000000000000..55a039e26998 --- /dev/null +++ b/lldb/tools/debugserver/source/DNB.h @@ -0,0 +1,141 @@ +//===-- DNB.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/23/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNB_h__ +#define __DNB_h__ + +#include "DNBDefs.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DNB_EXPORT __attribute__((visibility("default"))) + +typedef bool (*DNBShouldCancelCallback) (void *); + +//---------------------------------------------------------------------- +// Process control +//---------------------------------------------------------------------- +nub_process_t DNBProcessLaunch (const char *path, char const *argv[], const char *envp[], const char *stdio_path, nub_launch_flavor_t launch_flavor, char *err_str, size_t err_len) DNB_EXPORT; +nub_process_t DNBProcessAttach (nub_process_t pid, struct timespec *timeout, char *err_str, size_t err_len) DNB_EXPORT; +nub_process_t DNBProcessAttachByName (const char *name, struct timespec *timeout, char *err_str, size_t err_len) DNB_EXPORT; +nub_process_t DNBProcessAttachWait (const char *wait_name, nub_launch_flavor_t launch_flavor, struct timespec *timeout, useconds_t interval, char *err_str, size_t err_len, DNBShouldCancelCallback should_cancel = NULL, void *callback_data = NULL) DNB_EXPORT; +// Resume a process with exact instructions on what to do with each thread: +// - If no thread actions are supplied (actions is NULL or num_actions is zero), +// then all threads are continued. +// - If any thread actions are supplied, then each thread will do as it is told +// by the action. A default actions for any threads that don't have an +// explicit thread action can be made by making a thread action with a tid of +// INVALID_NUB_THREAD. If there is no default action, those threads will +// remain stopped. +nub_bool_t DNBProcessResume (nub_process_t pid, const DNBThreadResumeAction *actions, size_t num_actions) DNB_EXPORT; +nub_bool_t DNBProcessHalt (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessDetach (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessSignal (nub_process_t pid, int signal) DNB_EXPORT; +nub_bool_t DNBProcessKill (nub_process_t pid) DNB_EXPORT; +nub_size_t DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf) DNB_EXPORT; +nub_size_t DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub_size_t size, const void *buf) DNB_EXPORT; +nub_addr_t DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions) DNB_EXPORT; +nub_bool_t DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr) DNB_EXPORT; +//---------------------------------------------------------------------- +// Process status +//---------------------------------------------------------------------- +nub_bool_t DNBProcessIsAlive (nub_process_t pid) DNB_EXPORT; +nub_state_t DNBProcessGetState (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessGetExitStatus (nub_process_t pid, int *status) DNB_EXPORT; +nub_bool_t DNBProcessSetExitStatus (nub_process_t pid, int status) DNB_EXPORT; +nub_size_t DNBProcessGetNumThreads (nub_process_t pid) DNB_EXPORT; +nub_thread_t DNBProcessGetCurrentThread (nub_process_t pid) DNB_EXPORT; +nub_thread_t DNBProcessSetCurrentThread (nub_process_t pid, nub_thread_t tid) DNB_EXPORT; +nub_thread_t DNBProcessGetThreadAtIndex (nub_process_t pid, nub_size_t thread_idx) DNB_EXPORT; +nub_addr_t DNBProcessGetSharedLibraryInfoAddress (nub_process_t pid) DNB_EXPORT; +nub_bool_t DNBProcessSharedLibrariesUpdated (nub_process_t pid) DNB_EXPORT; +nub_size_t DNBProcessGetSharedLibraryInfo (nub_process_t pid, nub_bool_t only_changed, DNBExecutableImageInfo **image_infos) DNB_EXPORT; +nub_bool_t DNBProcessSetNameToAddressCallback (nub_process_t pid, DNBCallbackNameToAddress callback, void *baton) DNB_EXPORT; +nub_bool_t DNBProcessSetSharedLibraryInfoCallback (nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback, void *baton) DNB_EXPORT; +nub_addr_t DNBProcessLookupAddress (nub_process_t pid, const char *name, const char *shlib) DNB_EXPORT; +nub_size_t DNBProcessGetAvailableSTDOUT (nub_process_t pid, char *buf, nub_size_t buf_size) DNB_EXPORT; +nub_size_t DNBProcessGetAvailableSTDERR (nub_process_t pid, char *buf, nub_size_t buf_size) DNB_EXPORT; +nub_size_t DNBProcessGetStopCount (nub_process_t pid) DNB_EXPORT; +//---------------------------------------------------------------------- +// Process executable and arguments +//---------------------------------------------------------------------- +const char * DNBProcessGetExecutablePath (nub_process_t pid) DNB_EXPORT; +const char * DNBProcessGetArgumentAtIndex (nub_process_t pid, nub_size_t idx) DNB_EXPORT; +nub_size_t DNBProcessGetArgumentCount (nub_process_t pid) DNB_EXPORT; + +//---------------------------------------------------------------------- +// Process events +//---------------------------------------------------------------------- +nub_event_t DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout) DNB_EXPORT; +void DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask) DNB_EXPORT; +void DNBProcessInterruptEvents (nub_process_t pid) DNB_EXPORT; + +//---------------------------------------------------------------------- +// Thread functions +//---------------------------------------------------------------------- +const char * DNBThreadGetName (nub_process_t pid, nub_thread_t tid) DNB_EXPORT; +nub_bool_t DNBThreadGetIdentifierInfo (nub_process_t pid, nub_thread_t tid, thread_identifier_info_data_t *ident_info) DNB_EXPORT; +nub_state_t DNBThreadGetState (nub_process_t pid, nub_thread_t tid) DNB_EXPORT; +nub_bool_t DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value) DNB_EXPORT; +nub_bool_t DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value) DNB_EXPORT; +nub_size_t DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len) DNB_EXPORT; +nub_size_t DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len) DNB_EXPORT; +nub_bool_t DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t set, const char *name, DNBRegisterValue *value) DNB_EXPORT; +nub_bool_t DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, DNBThreadStopInfo *stop_info) DNB_EXPORT; +const char * DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid) DNB_EXPORT; +//---------------------------------------------------------------------- +// Breakpoint functions +//---------------------------------------------------------------------- +nub_break_t DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware) DNB_EXPORT; +nub_bool_t DNBBreakpointClear (nub_process_t pid, nub_break_t breakID) DNB_EXPORT; +nub_ssize_t DNBBreakpointGetHitCount (nub_process_t pid, nub_break_t breakID) DNB_EXPORT; +nub_ssize_t DNBBreakpointGetIgnoreCount (nub_process_t pid, nub_break_t breakID) DNB_EXPORT; +nub_bool_t DNBBreakpointSetIgnoreCount (nub_process_t pid, nub_break_t breakID, nub_size_t ignore_count) DNB_EXPORT; +nub_bool_t DNBBreakpointSetCallback (nub_process_t pid, nub_break_t breakID, DNBCallbackBreakpointHit callback, void *baton) DNB_EXPORT; +void DNBBreakpointPrint (nub_process_t pid, nub_break_t breakID) DNB_EXPORT; + +//---------------------------------------------------------------------- +// Watchpoint functions +//---------------------------------------------------------------------- +nub_watch_t DNBWatchpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, uint32_t watch_flags, nub_bool_t hardware) DNB_EXPORT; +nub_bool_t DNBWatchpointClear (nub_process_t pid, nub_watch_t watchID) DNB_EXPORT; +nub_ssize_t DNBWatchpointGetHitCount (nub_process_t pid, nub_watch_t watchID) DNB_EXPORT; +nub_ssize_t DNBWatchpointGetIgnoreCount (nub_process_t pid, nub_watch_t watchID) DNB_EXPORT; +nub_bool_t DNBWatchpointSetIgnoreCount (nub_process_t pid, nub_watch_t watchID, nub_size_t ignore_count) DNB_EXPORT; +nub_bool_t DNBWatchpointSetCallback (nub_process_t pid, nub_watch_t watchID, DNBCallbackBreakpointHit callback, void *baton) DNB_EXPORT; +void DNBWatchpointPrint (nub_process_t pid, nub_watch_t watchID) DNB_EXPORT; + +const DNBRegisterSetInfo * + DNBGetRegisterSetInfo (nub_size_t *num_reg_sets) DNB_EXPORT; +nub_bool_t DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info) DNB_EXPORT; + +//---------------------------------------------------------------------- +// Printf style formatting for printing values in the inferior memory +// space and registers. +//---------------------------------------------------------------------- +nub_size_t DNBPrintf (nub_process_t pid, nub_thread_t tid, nub_addr_t addr, FILE *file, const char *format) DNB_EXPORT; + +//---------------------------------------------------------------------- +// Other static nub information calls. +//---------------------------------------------------------------------- +const char * DNBStateAsString (nub_state_t state) DNB_EXPORT; +nub_bool_t DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size) DNB_EXPORT; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lldb/tools/debugserver/source/DNBArch.h b/lldb/tools/debugserver/source/DNBArch.h new file mode 100644 index 000000000000..3eb2362aa0f1 --- /dev/null +++ b/lldb/tools/debugserver/source/DNBArch.h @@ -0,0 +1,61 @@ +//===-- DNBArch.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/24/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DebugNubArch_h__ +#define __DebugNubArch_h__ + +#include "DNBDefs.h" +#include "MacOSX/MachException.h" + +#include +#include + +struct DNBRegisterValue; +struct DNBRegisterSetInfo; + +class DNBArchProtocol +{ +public: + static const DNBRegisterSetInfo * + GetRegisterSetInfo (nub_size_t *num_reg_sets); + + virtual bool GetRegisterValue (int set, int reg, DNBRegisterValue *value) = 0; + virtual bool SetRegisterValue (int set, int reg, const DNBRegisterValue *value) = 0; + virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len) = 0; + virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len) = 0; + + virtual kern_return_t GetRegisterState (int set, bool force) = 0; + virtual kern_return_t SetRegisterState (int set) = 0; + virtual bool RegisterSetStateIsValid (int set) const = 0; + + virtual uint64_t GetPC (uint64_t failValue) = 0; // Get program counter + virtual uint64_t GetSP (uint64_t failValue) = 0; // Get stack pointer + virtual void ThreadWillResume () = 0; + virtual bool ThreadDidStop () = 0; + virtual bool NotifyException (MachException::Data& exc) { return false; } + virtual uint32_t NumSupportedHardwareBreakpoints() { return 0; } + virtual uint32_t NumSupportedHardwareWatchpoints() { return 0; } + virtual uint32_t EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size) { return INVALID_NUB_HW_INDEX; } + virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write) { return INVALID_NUB_HW_INDEX; } + virtual bool DisableHardwareBreakpoint (uint32_t hw_index) { return false; } + virtual bool DisableHardwareWatchpoint (uint32_t hw_index) { return false; } + virtual bool StepNotComplete () { return false; } +}; + + +#include "MacOSX/arm/DNBArchImpl.h" +#include "MacOSX/i386/DNBArchImplI386.h" +#include "MacOSX/x86_64/DNBArchImplX86_64.h" +#include "MacOSX/ppc/DNBArchImpl.h" + +#endif diff --git a/lldb/tools/debugserver/source/DNBBreakpoint.cpp b/lldb/tools/debugserver/source/DNBBreakpoint.cpp new file mode 100644 index 000000000000..837f53260cfe --- /dev/null +++ b/lldb/tools/debugserver/source/DNBBreakpoint.cpp @@ -0,0 +1,303 @@ +//===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/29/07. +// +//===----------------------------------------------------------------------===// + +#include "DNBBreakpoint.h" +#include +#include "DNBLog.h" + + +#pragma mark -- DNBBreakpoint +DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size, nub_thread_t tid, bool hardware) : + m_breakID(GetNextID()), + m_tid(tid), + m_byte_size(byte_size), + m_opcode(), + m_addr(addr), + m_enabled(0), + m_hw_preferred(hardware), + m_is_watchpoint(0), + m_watch_read(0), + m_watch_write(0), + m_hw_index(INVALID_NUB_HW_INDEX), + m_hit_count(0), + m_ignore_count(0), + m_callback(NULL), + m_callback_baton(NULL) +{ +} + +DNBBreakpoint::~DNBBreakpoint() +{ +} + +nub_break_t +DNBBreakpoint::GetNextID() +{ + static uint32_t g_nextBreakID = 0; + return ++g_nextBreakID; +} + +void +DNBBreakpoint::SetCallback(DNBCallbackBreakpointHit callback, void *callback_baton) +{ + m_callback = callback; + m_callback_baton = callback_baton; +} + + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool +DNBBreakpoint::BreakpointHit(nub_process_t pid, nub_thread_t tid) +{ + m_hit_count++; + + if (m_hit_count > m_ignore_count) + { + if (m_callback) + return m_callback(pid, tid, GetID(), m_callback_baton); + return true; + } + return false; +} + +void +DNBBreakpoint::Dump() const +{ + if (IsBreakpoint()) + { + DNBLog("DNBBreakpoint %u: tid = %4.4x addr = %8.8p state = %s type = %s breakpoint hw_index = %i hit_count = %-4u ignore_count = %-4u callback = %8.8p baton = %8.8p", + m_breakID, + m_tid, + m_addr, + m_enabled ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + GetHardwareIndex(), + GetHitCount(), + GetIgnoreCount(), + m_callback, + m_callback_baton); + } + else + { + DNBLog("DNBBreakpoint %u: tid = %4.4x addr = %8.8p size = %u state = %s type = %s watchpoint (%s%s) hw_index = %i hit_count = %-4u ignore_count = %-4u callback = %8.8p baton = %8.8p", + m_breakID, + m_tid, + m_addr, + m_byte_size, + m_enabled ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + m_watch_read ? "r" : "", + m_watch_write ? "w" : "", + GetHardwareIndex(), + GetHitCount(), + GetIgnoreCount(), + m_callback, + m_callback_baton); + } +} + +#pragma mark -- DNBBreakpointList + +DNBBreakpointList::DNBBreakpointList() +{ +} + +DNBBreakpointList::~DNBBreakpointList() +{ +} + + +nub_break_t +DNBBreakpointList::Add(const DNBBreakpoint& bp) +{ + m_breakpoints.push_back(bp); + return m_breakpoints.back().GetID(); +} + +bool +DNBBreakpointList::ShouldStop(nub_process_t pid, nub_thread_t tid, nub_break_t breakID) +{ + DNBBreakpoint *bp = FindByID (breakID); + if (bp) + { + // Let the breakpoint 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->BreakpointHit(pid, tid); + } + // We should stop here since this breakpoint isn't valid anymore or it + // doesn't exist. + return true; +} + +nub_break_t +DNBBreakpointList::FindIDByAddress (nub_addr_t addr) +{ + DNBBreakpoint *bp = FindByAddress (addr); + if (bp) + { + DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBBreakpointList::%s ( addr = 0x%8.8llx ) => %u", __FUNCTION__, (uint64_t)addr, bp->GetID()); + return bp->GetID(); + } + DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBBreakpointList::%s ( addr = 0x%8.8llx ) => NONE", __FUNCTION__, (uint64_t)addr); + return INVALID_NUB_BREAK_ID; +} + +bool +DNBBreakpointList::Remove (nub_break_t breakID) +{ + iterator pos = GetBreakIDIterator(breakID); // Predicate + if (pos != m_breakpoints.end()) + { + m_breakpoints.erase(pos); + return true; + } + return false; +} + + +class BreakpointIDMatches +{ +public: + BreakpointIDMatches (nub_break_t breakID) : m_breakID(breakID) {} + bool operator() (const DNBBreakpoint& bp) const + { + return m_breakID == bp.GetID(); + } + private: + const nub_break_t m_breakID; +}; + +class BreakpointAddressMatches +{ +public: + BreakpointAddressMatches (nub_addr_t addr) : m_addr(addr) {} + bool operator() (const DNBBreakpoint& bp) const + { + return m_addr == bp.Address(); + } + private: + const nub_addr_t m_addr; +}; + +DNBBreakpointList::iterator +DNBBreakpointList::GetBreakIDIterator (nub_break_t breakID) +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(breakID)); // Predicate +} + +DNBBreakpointList::const_iterator +DNBBreakpointList::GetBreakIDConstIterator (nub_break_t breakID) const +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(breakID)); // Predicate +} + +DNBBreakpoint * +DNBBreakpointList::FindByID (nub_break_t breakID) +{ + iterator pos = GetBreakIDIterator(breakID); + if (pos != m_breakpoints.end()) + return &(*pos); + + return NULL; +} + +const DNBBreakpoint * +DNBBreakpointList::FindByID (nub_break_t breakID) const +{ + const_iterator pos = GetBreakIDConstIterator(breakID); + if (pos != m_breakpoints.end()) + return &(*pos); + + return NULL; +} + +DNBBreakpoint * +DNBBreakpointList::FindByAddress (nub_addr_t addr) +{ + iterator end = m_breakpoints.end(); + iterator pos = std::find_if(m_breakpoints.begin(), end, // Search full range + BreakpointAddressMatches(addr)); // Predicate + if (pos != end) + return &(*pos); + + return NULL; +} + +const DNBBreakpoint * +DNBBreakpointList::FindByAddress (nub_addr_t addr) const +{ + const_iterator end = m_breakpoints.end(); + const_iterator pos = std::find_if(m_breakpoints.begin(), end, // Search full range + BreakpointAddressMatches(addr)); // Predicate + if (pos != end) + return &(*pos); + + return NULL; +} + +bool +DNBBreakpointList::SetCallback(nub_break_t breakID, DNBCallbackBreakpointHit callback, void *callback_baton) +{ + DNBBreakpoint *bp = FindByID (breakID); + if (bp) + { + bp->SetCallback(callback, callback_baton); + return true; + } + return false; +} + + +void +DNBBreakpointList::Dump() const +{ + const_iterator pos; + const_iterator end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos).Dump(); +} + + +DNBBreakpoint * +DNBBreakpointList::GetByIndex (uint32_t i) +{ + iterator end = m_breakpoints.end(); + iterator pos; + uint32_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + return &(*pos); + } + return NULL; +} + +const DNBBreakpoint * +DNBBreakpointList::GetByIndex (uint32_t i) const +{ + const_iterator end = m_breakpoints.end(); + const_iterator pos; + uint32_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + return &(*pos); + } + return NULL; +} + diff --git a/lldb/tools/debugserver/source/DNBBreakpoint.h b/lldb/tools/debugserver/source/DNBBreakpoint.h new file mode 100644 index 000000000000..28c967a0525e --- /dev/null +++ b/lldb/tools/debugserver/source/DNBBreakpoint.h @@ -0,0 +1,159 @@ +//===-- DNBBreakpoint.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/29/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBBreakpoint_h__ +#define __DNBBreakpoint_h__ + +#include + +#include "DNBDefs.h" + +class DNBBreakpoint +{ +public: + DNBBreakpoint(nub_addr_t m_addr, nub_size_t byte_size, nub_thread_t tid, bool hardware); + ~DNBBreakpoint(); + + nub_break_t GetID() const { return m_breakID; } + nub_size_t ByteSize() const { return m_byte_size; } + uint8_t * SavedOpcodeBytes() { return &m_opcode[0]; } + const uint8_t * + SavedOpcodeBytes() const { return &m_opcode[0]; } + nub_addr_t Address() const { return m_addr; } + nub_thread_t ThreadID() const { return m_tid; } + bool IsEnabled() const { return m_enabled; } + bool IntersectsRange(nub_addr_t addr, nub_size_t size, nub_addr_t *intersect_addr, nub_size_t *intersect_size, nub_size_t *opcode_offset) const + { + // We only use software traps for software breakpoints + if (IsBreakpoint() && IsEnabled() && !IsHardware()) + { + if (m_byte_size > 0) + { + const nub_addr_t bp_end_addr = m_addr + m_byte_size; + const nub_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; + } + void SetEnabled(uint32_t enabled) + { + if (!enabled) + SetHardwareIndex(INVALID_NUB_HW_INDEX); + m_enabled = enabled; + } + void SetIsWatchpoint (uint32_t type) + { + m_is_watchpoint = 1; + m_watch_read = (type & WATCH_TYPE_READ) != 0; + m_watch_write = (type & WATCH_TYPE_WRITE) != 0; + } + bool IsBreakpoint() const { return m_is_watchpoint == 0; } + bool IsWatchpoint() const { return m_is_watchpoint == 1; } + bool WatchpointRead() const { return m_watch_read != 0; } + bool WatchpointWrite() const { return m_watch_write != 0; } + bool HardwarePreferred() const { return m_hw_preferred; } + bool IsHardware() const { return m_hw_index != INVALID_NUB_HW_INDEX; } + uint32_t GetHardwareIndex() const { return m_hw_index; } + void SetHardwareIndex(uint32_t hw_index) { m_hw_index = hw_index; } +// StateType GetState() const { return m_state; } +// void SetState(StateType newState) { m_state = newState; } + int32_t GetHitCount() const { return m_hit_count; } + int32_t GetIgnoreCount() const { return m_ignore_count; } + void SetIgnoreCount(int32_t n) { m_ignore_count = n; } + bool BreakpointHit(nub_process_t pid, nub_thread_t tid); + void SetCallback(DNBCallbackBreakpointHit callback, void *callback_baton); + void Dump() const; + +private: + nub_break_t m_breakID; // The unique identifier for this breakpoint + nub_thread_t m_tid; // Thread ID for the breakpoint (can be INVALID_NUB_THREAD for all threads) + nub_size_t m_byte_size; // Length in bytes of the breakpoint if set in memory + uint8_t m_opcode[8]; // Saved opcode bytes + nub_addr_t m_addr; // Address of this breakpoint + uint32_t m_enabled:1, // Flags for this breakpoint + m_hw_preferred:1, // 1 if this point has been requested to be set using hardware (which may fail due to lack of resources) + m_is_watchpoint:1, // 1 if this is a watchpoint + 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 + uint32_t m_hw_index; // The hardware resource index for this breakpoint/watchpoint + int32_t m_hit_count; // Number of times this breakpoint has been hit + int32_t m_ignore_count; // Number of times to ignore this breakpoint + DNBCallbackBreakpointHit + m_callback; // Callback to call when this breakpoint gets hit + void * m_callback_baton; // Callback user data to pass to callback + + static nub_break_t GetNextID(); + +}; + + +class DNBBreakpointList +{ +public: + DNBBreakpointList(); + ~DNBBreakpointList(); + + nub_break_t Add (const DNBBreakpoint& bp); + nub_break_t FindIDByAddress (nub_addr_t addr); + bool ShouldStop (nub_process_t pid, nub_thread_t tid, nub_break_t breakID); + bool Remove (nub_break_t breakID); + bool SetCallback (nub_break_t breakID, DNBCallbackBreakpointHit callback, void *callback_baton); + DNBBreakpoint * FindByAddress (nub_addr_t addr); + const DNBBreakpoint * FindByAddress (nub_addr_t addr) const; + DNBBreakpoint * FindByID (nub_break_t breakID); + const DNBBreakpoint * FindByID (nub_break_t breakID) const; + void Dump () const; + + size_t Size() const { return m_breakpoints.size(); } + DNBBreakpoint * GetByIndex (uint32_t i); + const DNBBreakpoint * GetByIndex (uint32_t i) const; + +protected: + typedef std::list collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + iterator GetBreakIDIterator(nub_break_t breakID); + const_iterator GetBreakIDConstIterator(nub_break_t breakID) const; + collection m_breakpoints; +}; + +#endif + diff --git a/lldb/tools/debugserver/source/DNBDataRef.cpp b/lldb/tools/debugserver/source/DNBDataRef.cpp new file mode 100644 index 000000000000..271c205e0247 --- /dev/null +++ b/lldb/tools/debugserver/source/DNBDataRef.cpp @@ -0,0 +1,485 @@ +//===-- DNBDataRef.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/11/06. +// +//===----------------------------------------------------------------------===// + +#include "DNBDataRef.h" +#include "DNBLog.h" +#include +#include +#include + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- + +DNBDataRef::DNBDataRef() : + m_start(NULL), + m_end(NULL), + m_swap(false), + m_ptrSize(0), + m_addrPCRelative(INVALID_NUB_ADDRESS), + m_addrTEXT(INVALID_NUB_ADDRESS), + m_addrDATA(INVALID_NUB_ADDRESS) +{ +} + + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- + +DNBDataRef::DNBDataRef(const uint8_t *start, size_t size, bool swap) : + m_start(start), + m_end(start+size), + m_swap(swap), + m_ptrSize(0), + m_addrPCRelative(INVALID_NUB_ADDRESS), + m_addrTEXT(INVALID_NUB_ADDRESS), + m_addrDATA(INVALID_NUB_ADDRESS) +{ +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- + +DNBDataRef::~DNBDataRef() +{ +} + + +//---------------------------------------------------------------------- +// Get8 +//---------------------------------------------------------------------- +uint8_t +DNBDataRef::Get8(offset_t *offset_ptr) const +{ + uint8_t val = 0; + if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) + { + val = *(m_start + *offset_ptr); + *offset_ptr += sizeof(val); + } + return val; +} + + +//---------------------------------------------------------------------- +// Get16 +//---------------------------------------------------------------------- +uint16_t +DNBDataRef::Get16(offset_t *offset_ptr) const +{ + uint16_t val = 0; + if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) + { + const uint8_t *p = m_start + *offset_ptr; + val = *(uint16_t*)p; + + if (m_swap) + val = OSSwapInt16(val); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return val; +} + + +//---------------------------------------------------------------------- +// Get32 +//---------------------------------------------------------------------- +uint32_t +DNBDataRef::Get32(offset_t *offset_ptr) const +{ + uint32_t val = 0; + if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) + { + const uint8_t *p = m_start + *offset_ptr; + val = *(uint32_t*)p; + if (m_swap) + val = OSSwapInt32(val); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return val; +} + + +//---------------------------------------------------------------------- +// Get64 +//---------------------------------------------------------------------- +uint64_t +DNBDataRef::Get64(offset_t *offset_ptr) const +{ + uint64_t val = 0; + if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) + { + const uint8_t *p = m_start + *offset_ptr; + val = *(uint64_t*)p; + if (m_swap) + val = OSSwapInt64(val); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return val; +} + + +//---------------------------------------------------------------------- +// GetMax32 +// +// Used for calls when the size can vary. Fill in extra cases if they +// are ever needed. +//---------------------------------------------------------------------- +uint32_t +DNBDataRef::GetMax32(offset_t *offset_ptr, uint32_t byte_size) const +{ + switch (byte_size) + { + case 1: return Get8 (offset_ptr); break; + case 2: return Get16(offset_ptr); break; + case 4: return Get32(offset_ptr); break; + default: + assert(!"GetMax32 unhandled case!"); + break; + } + return 0; +} + + +//---------------------------------------------------------------------- +// GetMax64 +// +// Used for calls when the size can vary. Fill in extra cases if they +// are ever needed. +//---------------------------------------------------------------------- +uint64_t +DNBDataRef::GetMax64(offset_t *offset_ptr, uint32_t size) const +{ + switch (size) + { + case 1: return Get8 (offset_ptr); break; + case 2: return Get16(offset_ptr); break; + case 4: return Get32(offset_ptr); break; + case 8: return Get64(offset_ptr); break; + default: + assert(!"GetMax64 unhandled case!"); + break; + } + return 0; +} + +//---------------------------------------------------------------------- +// GetPointer +// +// Extract a pointer value from the buffer. The pointer size must be +// set prior to using this using one of the SetPointerSize functions. +//---------------------------------------------------------------------- +uint64_t +DNBDataRef::GetPointer(offset_t *offset_ptr) const +{ + // Must set pointer size prior to using this call + assert(m_ptrSize != 0); + return GetMax64(offset_ptr, m_ptrSize); +} + +//---------------------------------------------------------------------- +// GetDwarfEHPtr +// +// Used for calls when the value type is specified by a DWARF EH Frame +// pointer encoding. +//---------------------------------------------------------------------- +/* +uint64_t +DNBDataRef::GetDwarfEHPtr(offset_t *offset_ptr, uint32_t encoding) const +{ + if (encoding == DW_EH_PE_omit) + return ULONG_LONG_MAX; // Value isn't in the buffer... + + uint64_t baseAddress = 0; + uint64_t addressValue = 0; + + BOOL signExtendValue = NO; + // Decode the base part or adjust our offset + switch (encoding & 0x70) + { + case DW_EH_PE_pcrel: + // SetEHPtrBaseAddresses should be called prior to extracting these + // so the base addresses are cached. + assert(m_addrPCRelative != INVALID_NUB_ADDRESS); + signExtendValue = YES; + baseAddress = *offset_ptr + m_addrPCRelative; + break; + + case DW_EH_PE_textrel: + // SetEHPtrBaseAddresses should be called prior to extracting these + // so the base addresses are cached. + assert(m_addrTEXT != INVALID_NUB_ADDRESS); + signExtendValue = YES; + baseAddress = m_addrTEXT; + break; + + case DW_EH_PE_datarel: + // SetEHPtrBaseAddresses should be called prior to extracting these + // so the base addresses are cached. + assert(m_addrDATA != INVALID_NUB_ADDRESS); + signExtendValue = YES; + baseAddress = m_addrDATA; + break; + + case DW_EH_PE_funcrel: + signExtendValue = YES; + break; + + case DW_EH_PE_aligned: + // SetPointerSize should be called prior to extracting these so the + // pointer size is cached + assert(m_ptrSize != 0); + if (m_ptrSize) + { + // Align to a address size boundary first + uint32_t alignOffset = *offset_ptr % m_ptrSize; + if (alignOffset) + offset_ptr += m_ptrSize - alignOffset; + } + break; + + default: + break; + } + + // Decode the value part + switch (encoding & DW_EH_PE_MASK_ENCODING) + { + case DW_EH_PE_absptr : addressValue = GetPointer(offset_ptr); break; + case DW_EH_PE_uleb128 : addressValue = Get_ULEB128(offset_ptr); break; + case DW_EH_PE_udata2 : addressValue = Get16(offset_ptr); break; + case DW_EH_PE_udata4 : addressValue = Get32(offset_ptr); break; + case DW_EH_PE_udata8 : addressValue = Get64(offset_ptr); break; + case DW_EH_PE_sleb128 : addressValue = Get_SLEB128(offset_ptr); break; + case DW_EH_PE_sdata2 : addressValue = (int16_t)Get16(offset_ptr); break; + case DW_EH_PE_sdata4 : addressValue = (int32_t)Get32(offset_ptr); break; + case DW_EH_PE_sdata8 : addressValue = (int64_t)Get64(offset_ptr); break; + default: + // Unhandled encoding type + assert(encoding); + break; + } + + // Since we promote everything to 64 bit, we may need to sign extend + if (signExtendValue && m_ptrSize < sizeof(baseAddress)) + { + uint64_t sign_bit = 1ull << ((m_ptrSize * 8ull) - 1ull); + if (sign_bit & addressValue) + { + uint64_t mask = ~sign_bit + 1; + addressValue |= mask; + } + } + return baseAddress + addressValue; +} +*/ + + +//---------------------------------------------------------------------- +// GetCStr +//---------------------------------------------------------------------- +const char * +DNBDataRef::GetCStr(offset_t *offset_ptr, uint32_t fixed_length) const +{ + const char *s = NULL; + if ( m_start < m_end ) + { + s = (char*)m_start + *offset_ptr; + + // Advance the offset + if (fixed_length) + *offset_ptr += fixed_length; + else + *offset_ptr += strlen(s) + 1; + } + return s; +} + + +//---------------------------------------------------------------------- +// GetData +//---------------------------------------------------------------------- +const uint8_t * +DNBDataRef::GetData(offset_t *offset_ptr, uint32_t length) const +{ + const uint8_t *data = NULL; + if ( length > 0 && ValidOffsetForDataOfSize(*offset_ptr, length) ) + { + data = m_start + *offset_ptr; + *offset_ptr += length; + } + return data; +} + + +//---------------------------------------------------------------------- +// Get_ULEB128 +//---------------------------------------------------------------------- +uint64_t +DNBDataRef::Get_ULEB128 (offset_t *offset_ptr) const +{ + uint64_t result = 0; + if ( m_start < m_end ) + { + int shift = 0; + const uint8_t *src = m_start + *offset_ptr; + uint8_t byte; + int bytecount = 0; + + while (src < m_end) + { + bytecount++; + byte = *src++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + + *offset_ptr += bytecount; + } + return result; +} + + +//---------------------------------------------------------------------- +// Get_SLEB128 +//---------------------------------------------------------------------- +int64_t +DNBDataRef::Get_SLEB128 (offset_t *offset_ptr) const +{ + int64_t result = 0; + + if ( m_start < m_end ) + { + int shift = 0; + int size = sizeof (uint32_t) * 8; + const uint8_t *src = m_start + *offset_ptr; + + uint8_t byte; + int bytecount = 0; + + while (src < m_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 |= - (1ll << shift); + + *offset_ptr += bytecount; + } + return result; +} + + +//---------------------------------------------------------------------- +// Skip_LEB128 +// +// Skips past ULEB128 and SLEB128 numbers (just updates the offset) +//---------------------------------------------------------------------- +void +DNBDataRef::Skip_LEB128 (offset_t *offset_ptr) const +{ + if ( m_start < m_end ) + { + const uint8_t *start = m_start + *offset_ptr; + const uint8_t *src = start; + + while ((src < m_end) && (*src++ & 0x80)) + /* Do nothing */; + + *offset_ptr += src - start; + } +} + +uint32_t +DNBDataRef::Dump +( + uint32_t startOffset, + uint32_t endOffset, + uint64_t offsetBase, + DNBDataRef::Type type, + uint32_t numPerLine, + const char *format +) +{ + uint32_t offset; + uint32_t count; + char str[1024]; + str[0] = '\0'; + int str_offset = 0; + + for (offset = startOffset, count = 0; ValidOffset(offset) && offset < endOffset; ++count) + { + if ((count % numPerLine) == 0) + { + // Print out any previous string + if (str[0] != '\0') + DNBLog("%s", str); + // Reset string offset and fill the current line string with address: + str_offset = 0; + str_offset += snprintf(str, sizeof(str), "0x%8.8llx:", (uint64_t)(offsetBase + (offset - startOffset))); + } + + // Make sure we don't pass the bounds of our current string buffer on each iteration through this loop + if (str_offset >= sizeof(str)) + { + // The last snprintf consumed our string buffer, we will need to dump this out + // and reset the string with no address + DNBLog("%s", str); + str_offset = 0; + str[0] = '\0'; + } + + // We already checked that there is at least some room in the string str above, so it is safe to make + // the snprintf call each time through this loop + switch (type) + { + default: + case TypeUInt8: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %2.2x", Get8(&offset)); break; + case TypeChar: + { + char ch = Get8(&offset); + str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %c", isprint(ch) ? ch : ' '); + } + break; + case TypeUInt16: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %4.4x", Get16(&offset)); break; + case TypeUInt32: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %8.8x", Get32(&offset)); break; + case TypeUInt64: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %16.16llx", Get64(&offset)); break; + case TypePointer: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " 0x%llx", GetPointer(&offset)); break; + case TypeULEB128: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " 0x%llx", Get_ULEB128(&offset)); break; + case TypeSLEB128: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %lld", Get_SLEB128(&offset)); break; + } + } + + if (str[0] != '\0') + DNBLog("%s", str); + + return offset; // Return the offset at which we ended up +} diff --git a/lldb/tools/debugserver/source/DNBDataRef.h b/lldb/tools/debugserver/source/DNBDataRef.h new file mode 100644 index 000000000000..fbecb7d4a76b --- /dev/null +++ b/lldb/tools/debugserver/source/DNBDataRef.h @@ -0,0 +1,111 @@ +//===-- DNBDataRef.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/11/06. +// +//===----------------------------------------------------------------------===// +// +// DNBDataRef is a class that can extract data in normal or byte +// swapped order from a data buffer that someone else owns. The data +// buffer needs to remain intact as long as the DNBDataRef object +// needs the data. Strings returned are pointers into the data buffer +// and will need to be copied if they are needed after the data buffer +// is no longer around. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBDataRef_h__ +#define __DNBDataRef_h__ + +#include "DNBDefs.h" +#include +#include +#include +#include + +class DNBDataRef +{ +public: + // For use with Dump + typedef enum + { + TypeUInt8 = 0, + TypeChar, + TypeUInt16, + TypeUInt32, + TypeUInt64, + TypePointer, + TypeULEB128, + TypeSLEB128 + } Type; + typedef uint32_t offset_t; + typedef nub_addr_t addr_t; + + DNBDataRef(); + DNBDataRef(const uint8_t *start, size_t size, bool swap); + ~DNBDataRef(); + void Clear() + { + DNBDataRef::SetData(NULL, 0); + m_swap = false; + } + + bool ValidOffset(offset_t offset) const { return (m_start < m_end) && ((uint32_t)(m_end - m_start) > offset); } + bool ValidOffsetForDataOfSize(offset_t offset, uint32_t num_bytes) const { return (m_start < m_end) && ((uint32_t)(m_end - m_start) > (offset + ((num_bytes > 0) ? (num_bytes - 1) : 0))); } + size_t GetSize() const { return m_end - m_start; } + const uint8_t * GetDataStart() const { return m_start; } + const uint8_t * GetDataEnd() const { return m_end; } + bool GetSwap() const { return m_swap; } + void SetSwap(bool swap) { m_swap = swap; } + void SetData(const uint8_t *start, size_t size) + { + m_start = start; + if (m_start != NULL) + m_end = start + size; + else + m_end = NULL; + } + uint8_t GetPointerSize() const { return m_ptrSize; } + void SetPointerSize(uint8_t size) { m_ptrSize = size; } + void SetEHPtrBaseAddrPCRelative(addr_t addr = INVALID_NUB_ADDRESS) { m_addrPCRelative = addr; } + void SetEHPtrBaseAddrTEXT(addr_t addr = INVALID_NUB_ADDRESS) { m_addrTEXT = addr; } + void SetEHPtrBaseAddrDATA(addr_t addr = INVALID_NUB_ADDRESS) { m_addrDATA = addr; } + uint8_t Get8(offset_t *offset_ptr) const; + uint16_t Get16(offset_t *offset_ptr) const; + uint32_t Get32(offset_t *offset_ptr) const; + uint64_t Get64(offset_t *offset_ptr) const; + uint32_t GetMax32(offset_t *offset_ptr, uint32_t byte_size) const; + uint64_t GetMax64(offset_t *offset_ptr, uint32_t byte_size) const; + uint64_t GetPointer(offset_t *offset_ptr) const; +// uint64_t GetDwarfEHPtr(offset_t *offset_ptr, uint32_t eh_ptr_enc) const; + const char * GetCStr(offset_t *offset_ptr, uint32_t fixed_length = 0) const; + const char * PeekCStr(offset_t offset) const + { + if (ValidOffset(offset)) + return (const char*)m_start + offset; + return NULL; + } + + const uint8_t * GetData( offset_t *offset_ptr, uint32_t length) const; + uint64_t Get_ULEB128 (offset_t *offset_ptr) const; + int64_t Get_SLEB128 (offset_t *offset_ptr) const; + void Skip_LEB128 (offset_t *offset_ptr) const; + + uint32_t Dump(offset_t startOffset, offset_t endOffset, uint64_t offsetBase, DNBDataRef::Type type, uint32_t numPerLine, const char *typeFormat = NULL); +protected: + const uint8_t * m_start; + const uint8_t * m_end; + bool m_swap; + uint8_t m_ptrSize; + addr_t m_addrPCRelative; + addr_t m_addrTEXT; + addr_t m_addrDATA; +}; + +#endif // #ifndef __DNBDataRef_h__ diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h new file mode 100644 index 000000000000..23960ef4957a --- /dev/null +++ b/lldb/tools/debugserver/source/DNBDefs.h @@ -0,0 +1,331 @@ +//===-- DNBDefs.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBDefs_h__ +#define __DNBDefs_h__ + +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------- +// Define nub_addr_t and the invalid address value from the architecture +//---------------------------------------------------------------------- +#if defined (__x86_64__) || defined (__ppc64__) + +//---------------------------------------------------------------------- +// 64 bit address architectures +//---------------------------------------------------------------------- +typedef uint64_t nub_addr_t; +#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ull) + +#elif defined (__i386__) || defined (__powerpc__) || defined (__ppc__) || defined (__arm__) + +//---------------------------------------------------------------------- +// 32 bit address architectures +//---------------------------------------------------------------------- + +typedef uint32_t nub_addr_t; +#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ul) + +#else + +//---------------------------------------------------------------------- +// Default to 64 bit address for unrecognized architectures. +//---------------------------------------------------------------------- + +#warning undefined architecture, defaulting to 8 byte addresses +typedef uint64_t nub_addr_t; +#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ull) + + +#endif + +typedef size_t nub_size_t; +typedef ssize_t nub_ssize_t; +typedef uint32_t nub_break_t; +typedef uint32_t nub_watch_t; +typedef uint32_t nub_index_t; +typedef pid_t nub_process_t; +typedef unsigned int nub_thread_t; +typedef uint32_t nub_event_t; +typedef uint32_t nub_bool_t; + +#define INVALID_NUB_BREAK_ID ((nub_break_t)0) +#define INVALID_NUB_PROCESS ((nub_process_t)0) +#define INVALID_NUB_THREAD ((nub_thread_t)0) +#define INVALID_NUB_HW_INDEX UINT32_MAX +#define INVALID_NUB_REGNUM UINT32_MAX +#define NUB_GENERIC_ERROR UINT32_MAX + +#define NUB_BREAK_ID_IS_VALID(breakID) ((breakID) != (INVALID_NUB_BREAK_ID)) + +// Watchpoint types +#define WATCH_TYPE_READ (1u << 0) +#define WATCH_TYPE_WRITE (1u << 1) + +typedef enum +{ + eStateInvalid = 0, + eStateUnloaded, + eStateAttaching, + eStateLaunching, + eStateStopped, + eStateRunning, + eStateStepping, + eStateCrashed, + eStateDetached, + eStateExited, + eStateSuspended +} nub_state_t; + +typedef enum +{ + eLaunchFlavorDefault = 0, + eLaunchFlavorPosixSpawn, + eLaunchFlavorForkExec, +#if defined (__arm__) + eLaunchFlavorSpringBoard, +#endif +} nub_launch_flavor_t; + +#define NUB_STATE_IS_RUNNING(s) ((s) == eStateAttaching ||\ + (s) == eStateLaunching ||\ + (s) == eStateRunning ||\ + (s) == eStateStepping ||\ + (s) == eStateDetached) + +#define NUB_STATE_IS_STOPPED(s) ((s) == eStateUnloaded ||\ + (s) == eStateStopped ||\ + (s) == eStateCrashed ||\ + (s) == eStateExited) + +enum +{ + eEventProcessRunningStateChanged = 1 << 0, // The process has changed state to running + eEventProcessStoppedStateChanged = 1 << 1, // The process has changed state to stopped + eEventSharedLibsStateChange = 1 << 2, // Shared libraries loaded/unloaded state has changed + eEventStdioAvailable = 1 << 3, // Something is available on stdout/stderr + eEventProcessAsyncInterrupt = 1 << 4, // Gives the ability for any infinite wait calls to be interrupted + kAllEventsMask = eEventProcessRunningStateChanged | + eEventProcessStoppedStateChanged | + eEventSharedLibsStateChange | + eEventStdioAvailable | + eEventProcessAsyncInterrupt +}; + +#define LOG_VERBOSE (1u << 0) +#define LOG_PROCESS (1u << 1) +#define LOG_THREAD (1u << 2) +#define LOG_EXCEPTIONS (1u << 3) +#define LOG_SHLIB (1u << 4) +#define LOG_MEMORY (1u << 5) // Log memory reads/writes calls +#define LOG_MEMORY_DATA_SHORT (1u << 6) // Log short memory reads/writes bytes +#define LOG_MEMORY_DATA_LONG (1u << 7) // Log all memory reads/writes bytes +#define LOG_MEMORY_PROTECTIONS (1u << 8) // Log memory protection changes +#define LOG_BREAKPOINTS (1u << 9) +#define LOG_EVENTS (1u << 10) +#define LOG_WATCHPOINTS (1u << 11) +#define LOG_STEP (1u << 12) +#define LOG_TASK (1u << 13) +#define LOG_LO_USER (1u << 16) +#define LOG_HI_USER (1u << 31) +#define LOG_ALL 0xFFFFFFFFu +#define LOG_DEFAULT ((LOG_PROCESS) |\ + (LOG_TASK) |\ + (LOG_THREAD) |\ + (LOG_EXCEPTIONS) |\ + (LOG_SHLIB) |\ + (LOG_MEMORY) |\ + (LOG_BREAKPOINTS) |\ + (LOG_WATCHPOINTS) |\ + (LOG_STEP)) + + +#define REGISTER_SET_ALL 0 +// Generic Register set to be defined by each architecture for access to common +// register values. +#define REGISTER_SET_GENERIC ((uint32_t)0xFFFFFFFFu) +#define GENERIC_REGNUM_PC 0 // Program Counter +#define GENERIC_REGNUM_SP 1 // Stack Pointer +#define GENERIC_REGNUM_FP 2 // Frame Pointer +#define GENERIC_REGNUM_RA 3 // Return Address +#define GENERIC_REGNUM_FLAGS 4 // Processor flags register + +enum DNBRegisterType +{ + InvalidRegType = 0, + Uint, // unsigned integer + Sint, // signed integer + IEEE754, // float + Vector // vector registers +}; + +enum DNBRegisterFormat +{ + InvalidRegFormat = 0, + Binary, + Decimal, + Hex, + Float, + VectorOfSInt8, + VectorOfUInt8, + VectorOfSInt16, + VectorOfUInt16, + VectorOfSInt32, + VectorOfUInt32, + VectorOfFloat32, + VectorOfUInt128 +}; + +struct DNBRegisterInfo +{ + uint32_t set; // Register set + uint32_t reg; // Register number + const char *name; // Name of this register + const char *alt; // Alternate name + uint16_t type; // Type of the register bits (DNBRegisterType) + uint16_t format; // Default format for display (DNBRegisterFormat), + uint32_t size; // Size in bytes of the register + uint32_t offset; // Offset from the beginning of the register context + uint32_t reg_gcc; // GCC register number (INVALID_NUB_REGNUM when none) + uint32_t reg_dwarf; // DWARF register number (INVALID_NUB_REGNUM when none) + uint32_t reg_generic; // Generic register number (INVALID_NUB_REGNUM when none) + uint32_t reg_gdb; // The GDB register number (INVALID_NUB_REGNUM when none) +}; + +struct DNBRegisterSetInfo +{ + const char *name; // Name of this register set + const struct DNBRegisterInfo *registers; // An array of register descriptions + nub_size_t num_registers; // The number of registers in REGISTERS array above +}; + +struct DNBThreadResumeAction +{ + nub_thread_t tid; // The thread ID that this action applies to, INVALID_NUB_THREAD for the default thread action + nub_state_t state; // Valid values are eStateStopped/eStateSuspended, eStateRunning, and eStateStepping. + int signal; // When resuming this thread, resume it with this signal + nub_addr_t addr; // If not INVALID_NUB_ADDRESS, then set the PC for the thread to ADDR before resuming/stepping +}; + +enum DNBThreadStopType +{ + eStopTypeInvalid = 0, + eStopTypeSignal, + eStopTypeException +}; + +enum DNBMemoryPermissions +{ + eMemoryPermissionsWritable = (1 << 0), + eMemoryPermissionsReadable = (1 << 1), + eMemoryPermissionsExecutable = (1 << 2) +}; + +#define DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH 256 +#define DNB_THREAD_STOP_INFO_MAX_EXC_DATA 8 + +//---------------------------------------------------------------------- +// DNBThreadStopInfo +// +// Describes the reason a thread stopped. +//---------------------------------------------------------------------- +struct DNBThreadStopInfo +{ + DNBThreadStopType reason; + char description[DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH]; + union + { + // eStopTypeSignal + struct + { + uint32_t signo; + } signal; + + // eStopTypeException + struct + { + uint32_t type; + nub_size_t data_count; + nub_addr_t data[DNB_THREAD_STOP_INFO_MAX_EXC_DATA]; + } exception; + } details; +}; + + +struct DNBRegisterValue +{ + struct DNBRegisterInfo info; // Register information for this register + union + { + int8_t sint8; + int16_t sint16; + int32_t sint32; + int64_t sint64; + uint8_t uint8; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; + float float32; + double float64; + int8_t v_sint8[16]; + int16_t v_sint16[8]; + int32_t v_sint32[4]; + int64_t v_sint64[2]; + uint8_t v_uint8[16]; + uint16_t v_uint16[8]; + uint32_t v_uint32[4]; + uint64_t v_uint64[2]; + float v_float32[4]; + double v_float64[2]; + void *pointer; + char *c_str; + } value; +}; + +enum DNBSharedLibraryState +{ + eShlibStateUnloaded = 0, + eShlibStateLoaded = 1 +}; + +#ifndef DNB_MAX_SEGMENT_NAME_LENGTH +#define DNB_MAX_SEGMENT_NAME_LENGTH 32 +#endif + +struct DNBSegment +{ + char name[DNB_MAX_SEGMENT_NAME_LENGTH]; + nub_addr_t addr; + nub_addr_t size; +}; + +struct DNBExecutableImageInfo +{ + char name[PATH_MAX]; // Name of the executable image (usually a full path) + uint32_t state; // State of the executable image (see enum DNBSharedLibraryState) + nub_addr_t header_addr; // Executable header address + uuid_t uuid; // Unique indentifier for matching with symbols + uint32_t num_segments; // Number of contiguous memory segments to in SEGMENTS array + DNBSegment *segments; // Array of contiguous memory segments in executable +}; + +typedef nub_bool_t (*DNBCallbackBreakpointHit)(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton); +typedef nub_addr_t (*DNBCallbackNameToAddress)(nub_process_t pid, const char *name, const char *shlib_regex, void *baton); +typedef nub_size_t (*DNBCallbackCopyExecutableImageInfos)(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton); +typedef void (*DNBCallbackLog)(void *baton, uint32_t flags, const char *format, va_list args); + +#endif // #ifndef __DNBDefs_h__ diff --git a/lldb/tools/debugserver/source/DNBError.cpp b/lldb/tools/debugserver/source/DNBError.cpp new file mode 100644 index 000000000000..8f6294bfb51e --- /dev/null +++ b/lldb/tools/debugserver/source/DNBError.cpp @@ -0,0 +1,108 @@ +//===-- DNBError.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#include "DNBError.h" +#include "CFString.h" +#include "DNBLog.h" +#include "PThreadMutex.h" + +#if defined (__arm__) +#include +#endif + +const char * +DNBError::AsString() const +{ + if (Success()) + return NULL; + + if (m_str.empty()) + { + const char *s = NULL; + switch (m_flavor) + { + case MachKernel: + s = ::mach_error_string (m_err); + break; + + case POSIX: + s = ::strerror (m_err); + break; + +#if defined (__arm__) + case SpringBoard: + { + CFStringRef statusStr = SBSApplicationLaunchingErrorString (m_err); + if (CFString::UTF8 (statusStr, m_str) == NULL) + m_str.clear(); + } + break; +#endif + default: + break; + } + if (s) + m_str.assign(s); + } + if (m_str.empty()) + return NULL; + return m_str.c_str(); +} + +void +DNBError::LogThreadedIfError(const char *format, ...) const +{ + 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 = AsString(); + if (err_str == NULL) + err_str = "???"; + DNBLogThreaded ("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_err); + free (arg_msg); + } + } +} + +void +DNBError::LogThreaded(const char *format, ...) const +{ + 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 = AsString(); + if (err_str == NULL) + err_str = "???"; + DNBLogThreaded ("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_err); + } + else + { + DNBLogThreaded ("%s err = 0x%8.8x", arg_msg, m_err); + } + free (arg_msg); + } +} diff --git a/lldb/tools/debugserver/source/DNBError.h b/lldb/tools/debugserver/source/DNBError.h new file mode 100644 index 000000000000..10c6638c7372 --- /dev/null +++ b/lldb/tools/debugserver/source/DNBError.h @@ -0,0 +1,96 @@ +//===-- DNBError.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBError_h__ +#define __DNBError_h__ + +#include +#include +#include +#include + +class DNBError +{ +public: + typedef uint32_t ValueType; + typedef enum + { + Generic = 0, + MachKernel, + POSIX +#if defined (__arm__) + , SpringBoard +#endif + } FlavorType; + + explicit DNBError( ValueType err = 0, + FlavorType flavor = Generic) : + m_err(err), + m_flavor(flavor) + { + } + + const char * AsString() const; + void Clear() { m_err = 0; m_flavor = Generic; m_str.clear(); } + ValueType Error() const { return m_err; } + FlavorType Flavor() const { return m_flavor; } + + ValueType operator = (kern_return_t err) + { + m_err = err; + m_flavor = MachKernel; + m_str.clear(); + return m_err; + } + + void SetError(kern_return_t err) + { + m_err = err; + m_flavor = MachKernel; + m_str.clear(); + } + + void SetErrorToErrno() + { + m_err = errno; + m_flavor = POSIX; + m_str.clear(); + } + + void SetError(ValueType err, FlavorType flavor) + { + m_err = err; + m_flavor = flavor; + m_str.clear(); + } + + // Generic errors can set their own string values + void SetErrorString(const char *err_str) + { + if (err_str && err_str[0]) + m_str = err_str; + else + m_str.clear(); + } + bool Success() const { return m_err == 0; } + bool Fail() const { return m_err != 0; } + void LogThreadedIfError(const char *format, ...) const; + void LogThreaded(const char *format, ...) const; +protected: + ValueType m_err; + FlavorType m_flavor; + mutable std::string m_str; +}; + + +#endif // #ifndef __DNBError_h__ diff --git a/lldb/tools/debugserver/source/DNBLog.cpp b/lldb/tools/debugserver/source/DNBLog.cpp new file mode 100644 index 000000000000..d99e415f37ee --- /dev/null +++ b/lldb/tools/debugserver/source/DNBLog.cpp @@ -0,0 +1,309 @@ +//===-- DNBLog.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#include "DNBLog.h" + +static int g_debug = 0; +static int g_verbose = 0; + +#if defined (DNBLOG_ENABLED) + +#include +#include +#include +#include +#include +#include +#include "PThreadMutex.h" + +uint32_t g_log_bits = 0; +static DNBCallbackLog g_log_callback = NULL; +static void *g_log_baton = NULL; + + +int +DNBLogGetDebug () +{ + return g_debug; +} + + +void +DNBLogSetDebug (int g) +{ + g_debug = g; +} + +int +DNBLogGetVerbose () +{ + return g_verbose; +} + +void +DNBLogSetVerbose (int v) +{ + g_verbose = v; +} + +bool +DNBLogCheckLogBit (uint32_t bit) +{ + return (g_log_bits & bit) != 0; +} + +uint32_t +DNBLogSetLogMask (uint32_t mask) +{ + uint32_t old = g_log_bits; + g_log_bits = mask; + return old; +} + +uint32_t +DNBLogGetLogMask () +{ + return g_log_bits; +} + +void +DNBLogSetLogCallback (DNBCallbackLog callback, void *baton) +{ + g_log_callback = callback; + g_log_baton = baton; +} + +bool +DNBLogEnabled () +{ + return g_log_callback != NULL; +} + +static inline void +_DNBLogVAPrintf(uint32_t flags, const char *format, va_list args) +{ + if (g_log_callback) + g_log_callback(g_log_baton, flags, format, args); +} + +void +_DNBLog(uint32_t flags, const char *format, ...) +{ + va_list args; + va_start (args, format); + _DNBLogVAPrintf(flags, format, args); + va_end (args); +} + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global g_debug is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +_DNBLogDebug (const char *format, ...) +{ + if (DNBLogEnabled () && g_debug) + { + va_list args; + va_start (args, format); + _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global g_debug is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +_DNBLogDebugVerbose (const char *format, ...) +{ + if (DNBLogEnabled () && g_debug && g_verbose) + { + va_list args; + va_start (args, format); + _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG | DNBLOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + + +static pthread_mutex_t * +GetLogThreadedMutex() +{ + static PThreadMutex g_LogThreadedMutex(PTHREAD_MUTEX_RECURSIVE); + return g_LogThreadedMutex.Mutex(); +} +static uint32_t g_message_id = 0; + +//---------------------------------------------------------------------- +// Prefix the formatted log string with process and thread IDs and +// suffix it with a newline. +//---------------------------------------------------------------------- +void +_DNBLogThreaded (const char *format, ...) +{ + if (DNBLogEnabled ()) + { + PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex()); + + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + _DNBLog (DNBLOG_FLAG_THREADED, "%u [%4.4x/%4.4x]: %s", ++g_message_id, getpid(), mach_thread_self(), arg_msg); + free (arg_msg); + } + } +} + +//---------------------------------------------------------------------- +// Prefix the formatted log string with process and thread IDs and +// suffix it with a newline. +//---------------------------------------------------------------------- +void +_DNBLogThreadedIf (uint32_t log_bit, const char *format, ...) +{ + if (DNBLogEnabled () && (log_bit & g_log_bits) == log_bit) + { + PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex()); + + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + _DNBLog (DNBLOG_FLAG_THREADED, "%u [%4.4x/%4.4x]: %s", ++g_message_id, getpid(), mach_thread_self(), arg_msg); + free (arg_msg); + } + } +} + + + +//---------------------------------------------------------------------- +// Printing of errors that are not fatal. +//---------------------------------------------------------------------- +void +_DNBLogError (const char *format, ...) +{ + if (DNBLogEnabled ()) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + _DNBLog (DNBLOG_FLAG_ERROR, "error: %s", arg_msg); + free (arg_msg); + } + } +} + +//---------------------------------------------------------------------- +// Printing of errors that ARE fatal. Exit with ERR exit code +// immediately. +//---------------------------------------------------------------------- +void +_DNBLogFatalError (int err, const char *format, ...) +{ + if (DNBLogEnabled ()) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + _DNBLog (DNBLOG_FLAG_ERROR | DNBLOG_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 +_DNBLogVerbose (const char *format, ...) +{ + if (DNBLogEnabled () && g_verbose) + { + va_list args; + va_start (args, format); + _DNBLogVAPrintf(DNBLOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +_DNBLogWarningVerbose (const char *format, ...) +{ + if (DNBLogEnabled () && g_verbose) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + _DNBLog (DNBLOG_FLAG_WARNING | DNBLOG_FLAG_VERBOSE, "warning: %s", arg_msg); + free (arg_msg); + } + } +} +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal. +//---------------------------------------------------------------------- +void +_DNBLogWarning (const char *format, ...) +{ + if (DNBLogEnabled ()) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + _DNBLog (DNBLOG_FLAG_WARNING, "warning: %s", arg_msg); + free (arg_msg); + } + } +} + +#endif diff --git a/lldb/tools/debugserver/source/DNBLog.h b/lldb/tools/debugserver/source/DNBLog.h new file mode 100644 index 000000000000..76ce0c55d37e --- /dev/null +++ b/lldb/tools/debugserver/source/DNBLog.h @@ -0,0 +1,96 @@ +//===-- DNBLog.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBLog_h__ +#define __DNBLog_h__ + +#include +#include +#include "DNBDefs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Flags that get filled in automatically before calling the log callback function +#define DNBLOG_FLAG_FATAL (1u << 0) +#define DNBLOG_FLAG_ERROR (1u << 1) +#define DNBLOG_FLAG_WARNING (1u << 2) +#define DNBLOG_FLAG_DEBUG (1u << 3) +#define DNBLOG_FLAG_VERBOSE (1u << 4) +#define DNBLOG_FLAG_THREADED (1u << 5) + +#define DNBLOG_ENABLED + +#if defined (DNBLOG_ENABLED) + +#define DNB_EXPORT __attribute__((visibility("default"))) + +void _DNBLog(uint32_t flags, const char *format, ...) DNB_EXPORT; +void _DNBLogDebug (const char *fmt, ...) DNB_EXPORT; +void _DNBLogDebugVerbose (const char *fmt, ...) DNB_EXPORT; +void _DNBLogThreaded (const char *fmt, ...) DNB_EXPORT; +void _DNBLogThreadedIf (uint32_t mask, const char *fmt, ...) DNB_EXPORT; +void _DNBLogError (const char *fmt, ...) DNB_EXPORT; +void _DNBLogFatalError (int err, const char *fmt, ...) DNB_EXPORT; +void _DNBLogVerbose (const char *fmt, ...) DNB_EXPORT; +void _DNBLogWarning (const char *fmt, ...) DNB_EXPORT; +void _DNBLogWarningVerbose (const char *fmt, ...) DNB_EXPORT; +bool DNBLogCheckLogBit (uint32_t bit) DNB_EXPORT; +uint32_t DNBLogSetLogMask (uint32_t mask) DNB_EXPORT; +uint32_t DNBLogGetLogMask () DNB_EXPORT; +void DNBLogSetLogCallback (DNBCallbackLog callback, void *baton) DNB_EXPORT; +bool DNBLogEnabled () DNB_EXPORT; +int DNBLogGetDebug () DNB_EXPORT; +void DNBLogSetDebug (int g) DNB_EXPORT; +int DNBLogGetVerbose () DNB_EXPORT; +void DNBLogSetVerbose (int g) DNB_EXPORT; + +#define DNBLog(fmt, ...) do { if (DNBLogEnabled()) { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogDebug(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogDebug(fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogDebugVerbose(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogDebugVerbose(fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogThreaded(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogThreaded(fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogThreadedIf(mask, fmt, ...) do { if (DNBLogEnabled()) { _DNBLogThreadedIf(mask, fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogError(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogError(fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogFatalError(err, fmt, ...) do { if (DNBLogEnabled()) { _DNBLogFatalError(err, fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogVerbose(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogVerbose(fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogWarning(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogWarning(fmt, ## __VA_ARGS__); } } while (0) +#define DNBLogWarningVerbose(fmt, ...) do { if (DNBLogEnabled()) { _DNBLogWarningVerbose(fmt, ## __VA_ARGS__); } } while (0) + +#else // #if defined(DNBLOG_ENABLED) + +#define DNBLogDebug(...) ((void)0) +#define DNBLogDebugVerbose(...) ((void)0) +#define DNBLogThreaded(...) ((void)0) +#define DNBLogThreadedIf(...) ((void)0) +#define DNBLogError(...) ((void)0) +#define DNBLogFatalError(...) ((void)0) +#define DNBLogVerbose(...) ((void)0) +#define DNBLogWarning(...) ((void)0) +#define DNBLogWarningVerbose(...) ((void)0) +#define DNBLogGetLogFile() ((FILE *)NULL) +#define DNBLogSetLogFile(f) ((void)0) +#define DNBLogCheckLogBit(bit) ((bool)false) +#define DNBLogSetLogMask(mask) ((uint32_t)0u) +#define DNBLogGetLogMask() ((uint32_t)0u) +#define DNBLogToASL() ((void)0) +#define DNBLogToFile() ((void)0) +#define DNBLogCloseLogFile() ((void)0) + +#endif // #else defined(DNBLOG_ENABLED) + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef __DNBLog_h__ diff --git a/lldb/tools/debugserver/source/DNBRegisterInfo.cpp b/lldb/tools/debugserver/source/DNBRegisterInfo.cpp new file mode 100644 index 000000000000..a8f09bc87840 --- /dev/null +++ b/lldb/tools/debugserver/source/DNBRegisterInfo.cpp @@ -0,0 +1,220 @@ +//===-- DNBRegisterInfo.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 8/3/07. +// +//===----------------------------------------------------------------------===// + +#include "DNBRegisterInfo.h" +#include "DNBLog.h" +#include + +DNBRegisterValueClass::DNBRegisterValueClass(const DNBRegisterInfo *regInfo) +{ + Clear(); + if (regInfo) + info = *regInfo; +} + +void +DNBRegisterValueClass::Clear() +{ + memset(&info, 0, sizeof(DNBRegisterInfo)); + memset(&value, 0, sizeof(value)); +} + +bool +DNBRegisterValueClass::IsValid() const +{ + return + info.name != NULL && + info.type != InvalidRegType && + info.size > 0 && info.size <= sizeof(value); +} + +#define PRINT_COMMA_SEPARATOR do { if (pos < end) { if (i > 0) { strncpy(pos, ", ", end - pos); pos += 2; } } } while (0) + +void +DNBRegisterValueClass::Dump(const char *pre, const char *post) const +{ + if (info.name != NULL) + { + int i; + char str[1024]; + char *pos; + char *end = str + sizeof(str); + if (info.format == Hex) + { + switch (info.size) + { + case 0: snprintf(str, sizeof(str), "%s", "error: invalid register size of zero."); break; + case 1: snprintf(str, sizeof(str), "0x%2.2x", value.uint8); break; + case 2: snprintf(str, sizeof(str), "0x%4.4x", value.uint16); break; + case 4: snprintf(str, sizeof(str), "0x%8.8x", value.uint32); break; + case 8: snprintf(str, sizeof(str), "0x%16.16llx", value.uint64); break; + case 16: snprintf(str, sizeof(str), "0x%16.16llx%16.16llx", value.v_uint64[0], value.v_uint64[1]); break; + default: + strncpy(str, "0x", 3); + pos = str + 2; + for (i=0; i 0) + { + switch (info.format) + { + case VectorOfSInt8: + snprintf(str, sizeof(str), "%s", "sint8 { "); + pos = str + strlen(str); + for (i=0; i +#include +#include "DNBDefs.h" + +struct DNBRegisterValueClass : public DNBRegisterValue +{ +#ifdef __cplusplus + DNBRegisterValueClass(const DNBRegisterInfo *regInfo = NULL); + void Clear(); + void Dump(const char *pre, const char *post) const; + bool IsValid() const; +#endif +}; + +#endif diff --git a/lldb/tools/debugserver/source/DNBRuntimeAction.h b/lldb/tools/debugserver/source/DNBRuntimeAction.h new file mode 100644 index 000000000000..d77bda8c604d --- /dev/null +++ b/lldb/tools/debugserver/source/DNBRuntimeAction.h @@ -0,0 +1,25 @@ +//===-- DNBRuntimeAction.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 10/8/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBRuntimeAction_h__ +#define __DNBRuntimeAction_h__ + +class DNBRuntimeAction +{ + virtual void Initialize (nub_process_t pid) = 0; + virtual void ProcessStateChanged (nub_state_t state) = 0; + virtual void SharedLibraryStateChanged (DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos) = 0; +}; + + +#endif // #ifndef __DNBRuntimeAction_h__ diff --git a/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp b/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp new file mode 100644 index 000000000000..b50dd0617843 --- /dev/null +++ b/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp @@ -0,0 +1,116 @@ +//===-- DNBThreadResumeActions.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 03/13/2010 +// +//===----------------------------------------------------------------------===// + +#include "DNBThreadResumeActions.h" + +DNBThreadResumeActions::DNBThreadResumeActions() : + m_actions (), + m_signal_handled () +{ +} + +DNBThreadResumeActions::DNBThreadResumeActions (const DNBThreadResumeAction *actions, size_t num_actions) : + m_actions (), + m_signal_handled () +{ + if (actions && num_actions) + { + m_actions.assign (actions, actions + num_actions); + m_signal_handled.assign (num_actions, false); + } +} + +DNBThreadResumeActions::DNBThreadResumeActions (nub_state_t default_action, int signal) : + m_actions(), + m_signal_handled () +{ + SetDefaultThreadActionIfNeeded (default_action, signal); +} + +void +DNBThreadResumeActions::Append (const DNBThreadResumeAction &action) +{ + m_actions.push_back (action); + m_signal_handled.push_back (false); +} + +void +DNBThreadResumeActions::AppendAction +( + nub_thread_t tid, + nub_state_t state, + int signal, + nub_addr_t addr +) +{ + DNBThreadResumeAction action = { tid, state, signal, addr }; + Append (action); +} + + +const DNBThreadResumeAction * +DNBThreadResumeActions::GetActionForThread (nub_thread_t tid, bool default_ok) const +{ + const size_t num_actions = m_actions.size(); + for (size_t i=0; i + +#include "DNBDefs.h" + + +class DNBThreadResumeActions +{ +public: + DNBThreadResumeActions (); + + DNBThreadResumeActions (nub_state_t default_action, int signal); + + DNBThreadResumeActions (const DNBThreadResumeAction *actions, size_t num_actions); + + bool + IsEmpty() const + { + return m_actions.empty(); + } + + void + Append (const DNBThreadResumeAction &action); + + void + AppendAction (nub_thread_t tid, + nub_state_t state, + int signal = 0, + nub_addr_t addr = INVALID_NUB_ADDRESS); + + void + AppendResumeAll () + { + AppendAction (INVALID_NUB_THREAD, eStateRunning); + } + + void + AppendSuspendAll () + { + AppendAction (INVALID_NUB_THREAD, eStateStopped); + } + + void + AppendStepAll () + { + AppendAction (INVALID_NUB_THREAD, eStateStepping); + } + + const DNBThreadResumeAction * + GetActionForThread (nub_thread_t tid, bool default_ok) const; + + size_t + NumActionsWithState (nub_state_t state) const; + + bool + SetDefaultThreadActionIfNeeded (nub_state_t action, int signal); + + void + SetSignalHandledForThread (nub_thread_t tid) const; + + const DNBThreadResumeAction * + GetFirst() const + { + return m_actions.data(); + } + + size_t + GetSize () const + { + return m_actions.size(); + } + +protected: + std::vector m_actions; + mutable std::vector m_signal_handled; +}; + + +#endif // #ifndef __DNBThreadResumeActions_h__ diff --git a/lldb/tools/debugserver/source/DNBTimer.h b/lldb/tools/debugserver/source/DNBTimer.h new file mode 100644 index 000000000000..a78b80f606d5 --- /dev/null +++ b/lldb/tools/debugserver/source/DNBTimer.h @@ -0,0 +1,162 @@ +//===-- DNBTimer.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/13/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBTimer_h__ +#define __DNBTimer_h__ + +#include +#include +#include +#include "PThreadMutex.h" + +class DNBTimer +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + DNBTimer (bool threadSafe) : + m_mutexAP() + { + if (threadSafe) + m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE)); + Reset(); + } + + DNBTimer (const DNBTimer& rhs) : + m_mutexAP() + { + // Create a new mutex to make this timer thread safe as well if + // the timer we are copying is thread safe + if (rhs.IsThreadSafe()) + m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE)); + m_timeval = rhs.m_timeval; + } + + DNBTimer& operator= (const DNBTimer& rhs) + { + // Create a new mutex to make this timer thread safe as well if + // the timer we are copying is thread safe + if (rhs.IsThreadSafe()) + m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE)); + m_timeval = rhs.m_timeval; + return *this; + } + + ~DNBTimer () + { + } + + bool + IsThreadSafe() const + { + return m_mutexAP.get() != NULL; + } + //------------------------------------------------------------------ + // Reset the time value to now + //------------------------------------------------------------------ + void + Reset () + { + PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get()); + gettimeofday (&m_timeval, NULL); + } + //------------------------------------------------------------------ + // Get the total mircoseconds since Jan 1, 1970 + //------------------------------------------------------------------ + uint64_t + TotalMicroSeconds () const + { + PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get()); + return (uint64_t)(m_timeval.tv_sec) * 1000000ull + (uint64_t)m_timeval.tv_usec; + } + + void + GetTime (uint32_t& sec, uint32_t& usec) const + { + PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get()); + sec = m_timeval.tv_sec; + usec = m_timeval.tv_usec; + } + //------------------------------------------------------------------ + // Return the number of microseconds elapsed between now and the + // m_timeval + //------------------------------------------------------------------ + uint64_t + ElapsedMicroSeconds (bool update) + { + PTHREAD_MUTEX_LOCKER (locker, m_mutexAP.get()); + struct timeval now; + gettimeofday (&now, NULL); + uint64_t now_usec = (uint64_t)(now.tv_sec) * 1000000ull + (uint64_t)now.tv_usec; + uint64_t this_usec = (uint64_t)(m_timeval.tv_sec) * 1000000ull + (uint64_t)m_timeval.tv_usec; + uint64_t elapsed = now_usec - this_usec; + // Update the timer time value if requeseted + if (update) + m_timeval = now; + return elapsed; + } + + static uint64_t GetTimeOfDay() + { + struct timeval now; + gettimeofday (&now, NULL); + uint64_t now_usec = (uint64_t)(now.tv_sec) * 1000000ull + (uint64_t)now.tv_usec; + return now_usec; + } + + static void OffsetTimeOfDay (struct timespec* ts, __darwin_time_t sec_offset = 0, long nsec_offset = 0) + { + if (ts == NULL) + return; + // Get the current time in a timeval structure + struct timeval now; + gettimeofday (&now, NULL); + // Morph it into a timespec + TIMEVAL_TO_TIMESPEC(&now, ts); + // Offset the timespec if requested + if (sec_offset != 0 || nsec_offset != 0) + { + // Offset the nano seconds + ts->tv_nsec += nsec_offset; + // Offset the seconds taking into account a nano-second overflow + ts->tv_sec = ts->tv_sec + ts->tv_nsec / 1000000000 + sec_offset; + // Trim the nanoseconds back there was an overflow + ts->tv_nsec = ts->tv_nsec % 1000000000; + } + } + static bool TimeOfDayLaterThan (struct timespec &ts) + { + struct timespec now; + OffsetTimeOfDay(&now); + if (now.tv_sec > ts.tv_sec) + return true; + else if (now.tv_sec < ts.tv_sec) + return false; + else + { + if (now.tv_nsec > ts.tv_nsec) + return true; + else + return false; + } + } +protected: + //------------------------------------------------------------------ + // Classes that inherit from DNBTimer can see and modify these + //------------------------------------------------------------------ + std::auto_ptr m_mutexAP; + struct timeval m_timeval; +}; + +#endif // #ifndef __DNBTimer_h__ diff --git a/lldb/tools/debugserver/source/FunctionProfiler.cpp b/lldb/tools/debugserver/source/FunctionProfiler.cpp new file mode 100644 index 000000000000..2fa57d3a7707 --- /dev/null +++ b/lldb/tools/debugserver/source/FunctionProfiler.cpp @@ -0,0 +1,288 @@ +//===-- FunctionProfiler.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 10/8/08. +// +//===----------------------------------------------------------------------===// + +#include "FunctionProfiler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "DNB.h" + +// Project includes + +//---------------------------------------------------------------------- +// FunctionProfiler constructor +//---------------------------------------------------------------------- +FunctionProfiler::FunctionProfiler(nub_addr_t start_addr, nub_addr_t stop_addr) : + m_pid(INVALID_NUB_PROCESS), + m_start_addr(start_addr), + m_stop_addr(stop_addr), + m_start_break_id(INVALID_NUB_BREAK_ID), + m_stop_break_id(INVALID_NUB_BREAK_ID), + m_func_entered_count(0), + m_last_pc(0), + m_last_flags(0), + m_consecutive_opcode_count(0), + m_total_opcode_count(0) +{ +} + + +FunctionProfiler::~FunctionProfiler() +{ + Clear(); +} + + +void +FunctionProfiler::Clear() +{ + if (m_pid != INVALID_NUB_PROCESS) + { + if (m_start_break_id != INVALID_NUB_BREAK_ID) + DNBBreakpointClear(m_pid, m_start_break_id); + if (m_stop_break_id != INVALID_NUB_BREAK_ID) + DNBBreakpointClear(m_pid, m_stop_break_id); + } + m_start_break_id = INVALID_NUB_BREAK_ID; + m_stop_break_id = INVALID_NUB_BREAK_ID; + m_func_entered_count = 0; + m_last_pc = 0; + m_last_flags = 0; + m_consecutive_opcode_count = 0; +} + +void +FunctionProfiler::Initialize(nub_process_t pid) +{ + //printf("FunctionProfiler::%s(0x%4.4x)\n", __FUNCTION__, pid); + Clear(); + m_pid = pid; +} + +#include "DNBDataRef.h" + +void +FunctionProfiler::SetBreakpoints() +{ +#if defined (__i386__) + nub_size_t bp_opcode_size = 1; +#elif defined (__powerpc__) || defined (__ppc__) + nub_size_t bp_opcode_size = 4; +#endif + if (m_start_addr != INVALID_NUB_ADDRESS && !NUB_BREAK_ID_IS_VALID(m_start_break_id)) + { +#if defined (__arm__) + m_start_break_id = DNBBreakpointSet(m_pid, m_start_addr & 0xFFFFFFFEu, m_start_addr & 1 ? 2 : 4, false); +#else + m_start_break_id = DNBBreakpointSet(m_pid, m_start_addr, bp_opcode_size, false); +#endif + if (NUB_BREAK_ID_IS_VALID(m_start_break_id)) + DNBBreakpointSetCallback(m_pid, m_start_break_id, FunctionProfiler::BreakpointHitCallback, this); + } + if (m_stop_addr != INVALID_NUB_ADDRESS && !NUB_BREAK_ID_IS_VALID(m_stop_break_id)) + { +#if defined (__arm__) + m_stop_break_id = DNBBreakpointSet(m_pid, m_stop_addr & 0xFFFFFFFEu, m_stop_addr & 1 ? 2 : 4, false); +#else + m_stop_break_id = DNBBreakpointSet(m_pid, m_stop_addr, bp_opcode_size, false); +#endif + if (NUB_BREAK_ID_IS_VALID(m_stop_break_id)) + DNBBreakpointSetCallback(m_pid, m_stop_break_id, FunctionProfiler::BreakpointHitCallback, this); + } +} + +nub_bool_t +FunctionProfiler::BreakpointHitCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton) +{ + printf("FunctionProfiler::%s(pid = %4.4x, tid = %4.4x, breakID = %u, baton = %p)\n", __FUNCTION__, pid, tid, breakID, baton); + return ((FunctionProfiler*) baton)->BreakpointHit(pid, tid, breakID); +} + +nub_bool_t +FunctionProfiler::BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID) +{ + printf("FunctionProfiler::%s(pid = %4.4x, tid = %4.4x, breakID = %u)\n", __FUNCTION__, pid, tid, breakID); + if (breakID == m_start_break_id) + { + m_func_entered_count++; + printf("FunctionProfiler::%s(pid = %4.4x, tid = %4.4x, breakID = %u) START breakpoint hit (%u)\n", __FUNCTION__, pid, tid, breakID, m_func_entered_count); + } + else if (breakID == m_stop_break_id) + { + if (m_func_entered_count > 0) + m_func_entered_count--; + printf("FunctionProfiler::%s(pid = %4.4x, tid = %4.4x, breakID = %u) STOP breakpoint hit (%u)\n", __FUNCTION__, pid, tid, breakID, m_func_entered_count); + } + return true; +} + +void +FunctionProfiler::ProcessStateChanged(nub_state_t state) +{ +// printf("FunctionProfiler::%s(%s)\n", __FUNCTION__, DNBStateAsString(state)); + + switch (state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + break; + + case eStateDetached: + case eStateExited: + // No sense is clearing out breakpoints if our process has exited... + m_start_break_id = INVALID_NUB_BREAK_ID; + m_stop_break_id = INVALID_NUB_BREAK_ID; + printf("[0x%8.8x - 0x%8.8x) executed %u total opcodes.\n", m_total_opcode_count); + break; + + case eStateStopped: + // Keep trying find dyld each time we stop until we do + if (!NUB_BREAK_ID_IS_VALID(m_start_break_id)) + SetBreakpoints(); + + if (ShouldStepProcess()) + { + + // TODO: do logging/tracing here + nub_thread_t tid = DNBProcessGetCurrentThread(m_pid); + DNBRegisterValue reg; + m_total_opcode_count++; + + if (DNBThreadGetRegisterValueByID(m_pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_PC, ®)) + { + const nub_addr_t pc = reg.value.uint32; + +#if defined (__i386__) + uint8_t buf[16]; + uint32_t bytes_read = DNBProcessMemoryRead(m_pid, pc, 1, buf); + if (bytes_read == 1) + printf("0x%8.8x: %2.2x\n", pc, buf[0]); + else + printf("0x%8.8x: error: can't read opcode byte.\n", pc); + +// if (bytes_read > 0) +// { +// for (uint32_t i=0; i 0) + { + // printf(" x %u\n", m_consecutive_opcode_count); + } + m_consecutive_opcode_count = 1; + //printf("0x%8.8x: %-5s", pc, curr_pc_is_thumb ? "Thumb" : "ARM"); + //fflush(stdout); + } + m_last_flags = flags; + } +#else +#error undefined architecture +#endif + m_last_pc = pc; + } + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} diff --git a/lldb/tools/debugserver/source/FunctionProfiler.h b/lldb/tools/debugserver/source/FunctionProfiler.h new file mode 100644 index 000000000000..a66205606731 --- /dev/null +++ b/lldb/tools/debugserver/source/FunctionProfiler.h @@ -0,0 +1,70 @@ +//===-- FunctionProfiler.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 10/8/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __FunctionProfiler_h__ +#define __FunctionProfiler_h__ + +// C Includes + +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "DNBDefs.h" +#include "DNBRuntimeAction.h" +#include "PThreadMutex.h" + +class DNBBreakpoint; +class MachProcess; + +class FunctionProfiler : public DNBRuntimeAction +{ +public: + FunctionProfiler (nub_addr_t start_addr, nub_addr_t stop_addr); + virtual ~FunctionProfiler (); + + //------------------------------------------------------------------ + // DNBRuntimeAction required functions + //------------------------------------------------------------------ + virtual void Initialize(nub_process_t pid); + virtual void ProcessStateChanged(nub_state_t state); + virtual void SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos) {} + + nub_bool_t BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID); + bool ShouldStepProcess() const + { + return m_func_entered_count > 0; + } +protected: + static nub_bool_t BreakpointHitCallback (nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton); + void Clear(); + void SetBreakpoints(); + + nub_process_t m_pid; + nub_addr_t m_start_addr; + nub_addr_t m_stop_addr; + nub_break_t m_start_break_id; + nub_break_t m_stop_break_id; + uint32_t m_func_entered_count; + nub_addr_t m_last_pc; + uint32_t m_last_flags; + uint32_t m_consecutive_opcode_count; + uint32_t m_total_opcode_count; +}; + + +#endif // __FunctionProfiler_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp b/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp new file mode 100644 index 000000000000..a15755044c31 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp @@ -0,0 +1,87 @@ +//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#include "CFBundle.h" +#include "CFString.h" + +//---------------------------------------------------------------------- +// CFBundle constructor +//---------------------------------------------------------------------- +CFBundle::CFBundle(const char *path) : + CFReleaser(), + m_bundle_url() +{ + if (path && path[0]) + SetPath(path); +} + +//---------------------------------------------------------------------- +// CFBundle copy constructor +//---------------------------------------------------------------------- +CFBundle::CFBundle(const CFBundle& rhs) : + CFReleaser(rhs), + m_bundle_url(rhs.m_bundle_url) +{ + +} + +//---------------------------------------------------------------------- +// CFBundle copy constructor +//---------------------------------------------------------------------- +CFBundle& +CFBundle::operator=(const CFBundle& rhs) +{ + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFBundle::~CFBundle() +{ +} + +//---------------------------------------------------------------------- +// Set the path for a bundle by supplying a +//---------------------------------------------------------------------- +bool +CFBundle::SetPath (const char *path) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + // Release our old bundle and ULR + reset(); // This class is a CFReleaser + m_bundle_url.reset(); + // Make a CFStringRef from the supplied path + CFString cf_path; + cf_path.SetFileSystemRepresentation(path); + if (cf_path.get()) + { + // Make our Bundle URL + m_bundle_url.reset (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true)); + if (m_bundle_url.get()) + { + reset (::CFBundleCreate (alloc, m_bundle_url.get())); + } + } + return get() != NULL; +} + +CFStringRef +CFBundle::GetIdentifier () const +{ + CFBundleRef bundle = get(); + if (bundle != NULL) + return ::CFBundleGetIdentifier (bundle); + return NULL; +} diff --git a/lldb/tools/debugserver/source/MacOSX/CFBundle.h b/lldb/tools/debugserver/source/MacOSX/CFBundle.h new file mode 100644 index 000000000000..d980c0ba16ff --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFBundle.h @@ -0,0 +1,37 @@ +//===-- CFBundle.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __CFBundle_h__ +#define __CFBundle_h__ + +#include "CFUtils.h" + +class CFBundle : public CFReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFBundle(const char *path = NULL); + CFBundle(const CFBundle& rhs); + CFBundle& operator=(const CFBundle& rhs); + virtual ~CFBundle(); + + bool SetPath (const char *path); + CFStringRef GetIdentifier () const; + +protected: + CFReleaser m_bundle_url; +}; + +#endif // #ifndef __CFBundle_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/CFData.cpp b/lldb/tools/debugserver/source/MacOSX/CFData.cpp new file mode 100644 index 000000000000..94c93da544a4 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFData.cpp @@ -0,0 +1,85 @@ +//===-- CFData.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#include "CFData.h" + +//---------------------------------------------------------------------- +// CFData constructor +//---------------------------------------------------------------------- +CFData::CFData(CFDataRef data) : + CFReleaser(data) +{ + +} + +//---------------------------------------------------------------------- +// CFData copy constructor +//---------------------------------------------------------------------- +CFData::CFData(const CFData& rhs) : + CFReleaser(rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFData copy constructor +//---------------------------------------------------------------------- +CFData& +CFData::operator=(const CFData& rhs) + +{ + *this = rhs; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFData::~CFData() +{ +} + + +CFIndex +CFData::GetLength() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetLength (data); + return 0; +} + + +const uint8_t* +CFData::GetBytePtr() const +{ + CFDataRef data = get(); + if (data) + return CFDataGetBytePtr (data); + return NULL; +} + +CFDataRef +CFData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format) +{ + CFAllocatorRef alloc = kCFAllocatorDefault; + reset(); + CFReleaser stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc)); + ::CFWriteStreamOpen (stream.get()); + CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL); + if (len > 0) + reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten)); + ::CFWriteStreamClose (stream.get()); + return get(); +} + diff --git a/lldb/tools/debugserver/source/MacOSX/CFData.h b/lldb/tools/debugserver/source/MacOSX/CFData.h new file mode 100644 index 000000000000..2c9d65d3af72 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFData.h @@ -0,0 +1,39 @@ +//===-- CFData.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __CFData_h__ +#define __CFData_h__ + +#include "CFUtils.h" + +class CFData : public CFReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFData(CFDataRef data = NULL); + CFData(const CFData& rhs); + CFData& operator=(const CFData& rhs); + virtual ~CFData(); + + CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format); + const uint8_t* GetBytePtr () const; + CFIndex GetLength () const; +protected: + //------------------------------------------------------------------ + // Classes that inherit from CFData can see and modify these + //------------------------------------------------------------------ +}; + +#endif // #ifndef __CFData_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/CFString.cpp b/lldb/tools/debugserver/source/MacOSX/CFString.cpp new file mode 100644 index 000000000000..819024ca3bcb --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFString.cpp @@ -0,0 +1,201 @@ +//===-- CFString.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#include "CFString.h" +#include +#include + +//---------------------------------------------------------------------- +// CFString constructor +//---------------------------------------------------------------------- +CFString::CFString(CFStringRef s) : + CFReleaser (s) +{ +} + +//---------------------------------------------------------------------- +// CFString copy constructor +//---------------------------------------------------------------------- +CFString::CFString(const CFString& rhs) : + CFReleaser (rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFString copy constructor +//---------------------------------------------------------------------- +CFString& +CFString::operator=(const CFString& rhs) +{ + if (this != &rhs) + *this = rhs; + return *this; +} + +CFString::CFString (const char *cstr, CFStringEncoding cstr_encoding) : + CFReleaser () +{ + if (cstr && cstr[0]) + { + reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding)); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFString::~CFString() +{ +} + +const char * +CFString::GetFileSystemRepresentation(std::string& s) +{ + return CFString::FileSystemRepresentation(get(), s); +} + +CFStringRef +CFString::SetFileSystemRepresentation (const char *path) +{ + CFStringRef new_value = NULL; + if (path && path[0]) + new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path); + reset(new_value); + return get(); +} + + +CFStringRef +CFString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type) +{ + CFStringRef new_value = NULL; + if (cf_type != NULL) + { + CFTypeID cf_type_id = ::CFGetTypeID(cf_type); + + if (cf_type_id == ::CFStringGetTypeID()) + { + // Retain since we are using the existing object + new_value = (CFStringRef)::CFRetain(cf_type); + } + else if (cf_type_id == ::CFURLGetTypeID()) + { + new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle); + } + } + reset(new_value); + return get(); +} + +CFStringRef +CFString::SetFileSystemRepresentationAndExpandTilde (const char *path) +{ + std::string expanded_path; + if (CFString::GlobPath(path, expanded_path)) + SetFileSystemRepresentation(expanded_path.c_str()); + else + reset(); + return get(); +} + +const char * +CFString::UTF8(std::string& str) +{ + return CFString::UTF8(get(), str); +} + +// Static function that puts a copy of the UTF8 contents of CF_STR into STR +// and returns the C string pointer that is contained in STR when successful, else +// NULL is returned. This allows the std::string parameter to own the extracted string, +// and also allows that string to be returned as a C string pointer that can be used. + +const char * +CFString::UTF8 (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + const CFStringEncoding encoding = kCFStringEncodingUTF8; + CFIndex max_utf8_str_len = CFStringGetLength (cf_str); + max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding); + if (max_utf8_str_len > 0) + { + str.resize(max_utf8_str_len); + if (!str.empty()) + { + if (CFStringGetCString (cf_str, &str[0], str.size(), encoding)) + { + str.resize(strlen(str.c_str())); + return str.c_str(); + } + } + } + } + return NULL; +} + +// Static function that puts a copy of the file system representation of CF_STR +// into STR and returns the C string pointer that is contained in STR when +// successful, else NULL is returned. This allows the std::string parameter +// to own the extracted string, and also allows that string to be returned as +// a C string pointer that can be used. + +const char * +CFString::FileSystemRepresentation (CFStringRef cf_str, std::string& str) +{ + if (cf_str) + { + CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str); + if (max_length > 0) + { + str.resize(max_length); + if (!str.empty()) + { + if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size())) + { + str.erase(::strlen(str.c_str())); + return str.c_str(); + } + } + } + } + str.erase(); + return NULL; +} + + +CFIndex +CFString::GetLength() const +{ + CFStringRef str = get(); + if (str) + return CFStringGetLength (str); + return 0; +} + + +const char* +CFString::GlobPath(const char* path, std::string &expanded_path) +{ + glob_t globbuf; + if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0) + { + expanded_path = globbuf.gl_pathv[0]; + ::globfree (&globbuf); + } + else + expanded_path.clear(); + + return expanded_path.c_str(); +} + diff --git a/lldb/tools/debugserver/source/MacOSX/CFString.h b/lldb/tools/debugserver/source/MacOSX/CFString.h new file mode 100644 index 000000000000..2b96b8237fc9 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFString.h @@ -0,0 +1,43 @@ +//===-- CFString.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/16/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __CFString_h__ +#define __CFString_h__ + +#include "CFUtils.h" +#include + +class CFString : public CFReleaser +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CFString (CFStringRef cf_str = NULL); + CFString (const char *s, CFStringEncoding encoding); + CFString (const CFString& rhs); + CFString& operator= (const CFString& rhs); + virtual ~CFString (); + + const char * GetFileSystemRepresentation (std::string& str); + CFStringRef SetFileSystemRepresentation (const char *path); + CFStringRef SetFileSystemRepresentationFromCFType (CFTypeRef cf_type); + CFStringRef SetFileSystemRepresentationAndExpandTilde (const char *path); + const char * UTF8 (std::string& str); + CFIndex GetLength() const; + static const char *UTF8 (CFStringRef cf_str, std::string& str); + static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str); + static const char* GlobPath(const char* path, std::string &expanded_path); +}; + +#endif // #ifndef __CFString_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/CFUtils.h b/lldb/tools/debugserver/source/MacOSX/CFUtils.h new file mode 100644 index 000000000000..afa984fa11cd --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/CFUtils.h @@ -0,0 +1,81 @@ +//===-- CFUtils.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/5/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __CFUtils_h__ +#define __CFUtils_h__ + +#include + +#ifdef __cplusplus + +//---------------------------------------------------------------------- +// Templatized CF helper class that can own any CF pointer and will +// call CFRelease() on any valid pointer it owns unless that pointer is +// explicitly released using the release() member function. +//---------------------------------------------------------------------- +template +class CFReleaser +{ +public: + // Type names for the avlue + typedef T element_type; + + // Constructors and destructors + CFReleaser(T ptr = NULL) : _ptr(ptr) { } + CFReleaser(const CFReleaser& copy) : _ptr(copy.get()) + { + if (get()) + ::CFRetain(get()); + } + virtual ~CFReleaser() { reset(); } + + // Assignments + CFReleaser& operator= (const CFReleaser& copy) + { + if (copy != *this) + { + // Replace our owned pointer with the new one + reset(copy.get()); + // Retain the current pointer that we own + if (get()) + ::CFRetain(get()); + } + } + // Get the address of the contained type + T * ptr_address() { return &_ptr; } + + // Access the pointer itself + const T get() const { return _ptr; } + T get() { return _ptr; } + + // Set a new value for the pointer and CFRelease our old + // value if we had a valid one. + void reset(T ptr = NULL) + { + if (ptr != _ptr) + { + if (_ptr != NULL) + ::CFRelease(_ptr); + _ptr = ptr; + } + } + + // Release ownership without calling CFRelease + T release() { T tmp = _ptr; _ptr = NULL; return tmp; } +private: + element_type _ptr; +}; + +#endif // #ifdef __cplusplus +#endif // #ifndef __CFUtils_h__ + diff --git a/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp b/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp new file mode 100644 index 000000000000..76288c55bf52 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachDYLD.cpp @@ -0,0 +1,679 @@ +//===-- MachDYLD.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/29/07. +// +//===----------------------------------------------------------------------===// + +#include "MachDYLD.h" +#include "DNB.h" +#include "DNBDataRef.h" +#include +#include "DNBLog.h" + +MachDYLD::MachDYLD() : + m_pid(INVALID_NUB_PROCESS), + m_addr_size(4), + m_dyld_addr(INVALID_NUB_ADDRESS), + m_dyld_all_image_infos_addr(INVALID_NUB_ADDRESS), + m_dylib_info_header(), + m_current_dylibs(), + m_changed_dylibs(), + m_notify_break_id(INVALID_NUB_BREAK_ID), + m_dyld_info_mutex(PTHREAD_MUTEX_RECURSIVE) +{ +} + +MachDYLD::~MachDYLD() +{ + Clear(); +} + + +void +MachDYLD::Clear() +{ + PThreadMutex::Locker locker(m_dyld_info_mutex); + + nub_process_t pid = m_pid; + if (pid != INVALID_NUB_PROCESS) + { + DNBProcessSetSharedLibraryInfoCallback ( pid, NULL, NULL); + DNBBreakpointClear(pid, m_notify_break_id); + } + + m_addr_size = 4; + m_dyld_addr = INVALID_NUB_ADDRESS; + m_dyld_all_image_infos_addr = INVALID_NUB_ADDRESS; + m_dylib_info_header.Clear(); + m_current_dylibs.clear(); + m_changed_dylibs.clear(); + m_notify_break_id = INVALID_NUB_BREAK_ID; +} + + +void +MachDYLD::Initialize(nub_process_t pid) +{ + //printf("MachDYLD::%s(0x%4.4x)\n", __FUNCTION__, pid); + Clear(); + m_pid = pid; +} + + +void +MachDYLD::ProcessStateChanged(nub_state_t state) +{ + //printf("MachDYLD::%s(%s)\n", __FUNCTION__, DNBStateAsString(state)); + + switch (state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + case eStateAttaching: + case eStateLaunching: + Clear(); + break; + + case eStateStopped: + // Keep trying find dyld each time we stop until we do + if (!FoundDYLD()) + { + assert(m_pid != INVALID_NUB_PROCESS); + DNBProcessSetSharedLibraryInfoCallback ( m_pid, CopySharedInfoCallback, this); + CheckForDYLDInMemory(); + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} + +void +MachDYLD::SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos) +{ + //printf("MachDYLD::%s(%p, %u)\n", __FUNCTION__, image_infos, image_infos); + +} + +bool +MachDYLD::FoundDYLD() const +{ + return m_dyld_addr != INVALID_NUB_ADDRESS; +} + +bool +MachDYLD::CheckForDYLDInMemory() +{ +#if defined (__arm__) + return CheckForDYLDInMemory(0x2fe00000); +#else + return CheckForDYLDInMemory(0x8fe00000); +#endif +} + +bool +MachDYLD::CheckForDYLDInMemory(nub_addr_t addr) +{ + std::vector dyld_header; + nub_size_t page_size = 0x1000; + dyld_header.resize(page_size); + nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, addr, dyld_header.size(), &dyld_header[0]); + if (bytes_read > 0) + { + DNBDataRef::offset_t offset = 0; + DNBDataRef data(&dyld_header[0], bytes_read, false); + struct mach_header *header = (struct mach_header*)data.GetData(&offset, sizeof(struct mach_header)); + if (header) + { + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetPointerSize(4); + m_addr_size = 4; + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetPointerSize(8); + m_addr_size = 8; + break; + + default: + return false; + } + + if (header->filetype == MH_DYLINKER) + { + // printf( "Found DYLD mach image at %8.8p", addr); + + m_dyld_all_image_infos_addr = DNBProcessLookupAddress(m_pid, "dyld_all_image_infos", "/usr/lib/dyld"); + +#if defined (__arm__) + m_dyld_all_image_infos_addr = 0x2fe3a004; +#endif + + if (m_dyld_all_image_infos_addr != INVALID_NUB_ADDRESS) + { + // printf( "Found DYLD data symbol 'dyld_all_image_infos' is %8.8p", m_dyld_all_image_infos_addr); + + if (ReadDYLIBInfo()) + { + if (m_dylib_info_header.notification != INVALID_NUB_ADDRESS) + { + m_notify_break_id = DNBBreakpointSet(m_pid, m_dylib_info_header.notification, 4, true); + if (NUB_BREAK_ID_IS_VALID(m_notify_break_id)) + { + DNBBreakpointSetCallback(m_pid, m_notify_break_id, MachDYLD::BreakpointHit, this); + m_dyld_addr = addr; + } + } + } + // if (DNBLogCheckLogBit(LOG_SHLIB)) + // Dump(DNBLogGetLogFile()); + } + return true; + } + } + } + return false; +} + +nub_bool_t +MachDYLD::BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton) +{ + MachDYLD *dyld = (MachDYLD*) baton; + //printf("MachDYLD::BreakpointHit called"); + dyld->ReadDYLIBInfo(); + DNBProcessSharedLibrariesUpdated(pid); + return false; // Don't stop the process, let it continue +} + +bool +MachDYLD::ReadDYLIBInfo() +{ + nub_addr_t addr = m_dyld_all_image_infos_addr; + if (addr != INVALID_NUB_ADDRESS) + { + PThreadMutex::Locker locker(m_dyld_info_mutex); + //printf("MachDYLD::ReadDYLIBInfo(addr =%8.8p)", addr); + bool swap = false; + uint32_t i = 0; + DYLIBInfo::collection previous_dylibs; + previous_dylibs.swap(m_current_dylibs); + uint8_t all_dylib_info_data[32]; + nub_size_t count = 8 + m_addr_size * 2; + nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, addr, count, &all_dylib_info_data[0]); + if (bytes_read != count) + { + m_dylib_info_header.Clear(); + return false; + } + + DNBDataRef data(all_dylib_info_data, sizeof(all_dylib_info_data), swap); + data.SetPointerSize(m_addr_size); + DNBDataRef::offset_t offset = 0; + m_dylib_info_header.version = data.Get32(&offset); + m_dylib_info_header.dylib_info_count = data.Get32(&offset); + m_dylib_info_header.dylib_info_addr = data.GetPointer(&offset); + m_dylib_info_header.notification = data.GetPointer(&offset); + //printf( "%s: version=%d, count=%d, addr=%8.8p, notify=%8.8p", + // __PRETTY_FUNCTION__, + // m_dylib_info_header.version, + // m_dylib_info_header.dylib_info_count, + // m_dylib_info_header.dylib_info_addr, + // m_dylib_info_header.notification); + + switch (m_dylib_info_header.version) + { + case 1: // 10.4.x and prior + { + } + break; + + case 2: // 10.5 and later + { + } + break; + + default: + //printf( "Invalid dyld all_dylib_infos version number: %d", m_dylib_info_header.version); + return false; + break; + } + + // If we made it here, we are assuming that the all dylib info data should + // be valid, lets read the info array. + if (m_dylib_info_header.dylib_info_count > 0) + { + if (m_dylib_info_header.dylib_info_addr == 0) + { + //printf( "dyld is currently updating all_dylib_infos."); + } + else + { + m_current_dylibs.resize(m_dylib_info_header.dylib_info_count); + count = m_current_dylibs.size() * 3 * m_addr_size; + std::vector info_data(count, 0); + bytes_read = DNBProcessMemoryRead(m_pid, m_dylib_info_header.dylib_info_addr, count, &info_data[0]); + if (bytes_read == count) + { + DNBDataRef::offset_t info_data_offset = 0; + DNBDataRef info_data_ref(&info_data[0], info_data.size(), swap); + info_data_ref.SetPointerSize(m_addr_size); + for (i = 0; info_data_ref.ValidOffset(info_data_offset); i++) + { + assert (i < m_current_dylibs.size()); + m_current_dylibs[i].address = info_data_ref.GetPointer(&info_data_offset); + nub_addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + m_current_dylibs[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + bytes_read = DNBProcessMemoryRead(m_pid, path_addr, sizeof(raw_path), (char*)&raw_path[0]); + if (::realpath(raw_path, resolved_path)) + m_current_dylibs[i].path = resolved_path; + else + m_current_dylibs[i].path = raw_path; + } + assert(i == m_dylib_info_header.dylib_info_count); + + UpdateUUIDs(); + } + else + { + //printf( "unable to read all data for all_dylib_infos."); + m_current_dylibs.clear(); + return false; + } + } + } + // Read any UUID values that we can get + if (m_current_dylibs.empty()) + { + m_changed_dylibs = previous_dylibs; + const size_t num_changed_dylibs = m_changed_dylibs.size(); + for (i = 0; i < num_changed_dylibs; i++) + { + // Indicate the shared library was unloaded by giving it an invalid + // address... + m_changed_dylibs[i].address = INVALID_NUB_ADDRESS; + } + } + else + { + m_changed_dylibs.clear(); + + // Most of the time when we get shared library changes, they just + // get appended to the end of the list, so find out the min number + // of entries in the current and previous list that match and see + // how many are equal. + uint32_t curr_dylib_count = m_current_dylibs.size(); + uint32_t prev_dylib_count = previous_dylibs.size(); + uint32_t common_count = std::min(prev_dylib_count, curr_dylib_count); + MachDYLD::DYLIBInfo::const_iterator curr_pos = m_current_dylibs.begin(); + MachDYLD::DYLIBInfo::const_iterator curr_end = m_current_dylibs.end(); + MachDYLD::DYLIBInfo::iterator prev_pos = previous_dylibs.begin(); + uint32_t idx; + for (idx = 0; idx < common_count; idx++) + { + if (*curr_pos == *prev_pos) + { + ++curr_pos; + ++prev_pos; + } + else + break; + } + + // Remove all the entries that were at the exact same index and that + // matched between the previous_dylibs and m_current_dylibs arrays. This will cover + // most of the cases as when shared libraries get loaded they get + // appended to the end of the list. + if (prev_pos != previous_dylibs.begin()) + { + previous_dylibs.erase(previous_dylibs.begin(), prev_pos); + } + + if (previous_dylibs.empty()) + { + // We only have new libraries to add, they are the only ones that + // have changed. + if (curr_pos != curr_end) + { + m_changed_dylibs.assign(curr_pos, curr_end); + } + } + else + { + // We still have items in our previous dylib list which means either + // one or more shared libraries got unloaded somewhere in the middle + // of the list, so we will manually search for each remaining item + // in our current list in the previous list + for (; curr_pos != curr_end; ++curr_pos) + { + MachDYLD::DYLIBInfo::iterator pos = std::find(previous_dylibs.begin(), previous_dylibs.end(), *curr_pos); + if (pos == previous_dylibs.end()) + { + // This dylib wasn't here before, add it to our change list + m_changed_dylibs.push_back(*curr_pos); + } + else + { + // This dylib was in our previous dylib list, it didn't + // change, so lets remove it from the previous list so we + // don't see it again. + previous_dylibs.erase(pos); + } + } + + // The only items left if our previous_dylibs array will be shared + // libraries that got unloaded (still in previous list, and not + // mentioned in the current list). + if (!previous_dylibs.empty()) + { + const size_t num_previous_dylibs = previous_dylibs.size(); + for (i = 0; i < num_previous_dylibs; i++) + { + // Indicate the shared library was unloaded by giving it + // an invalid address... + previous_dylibs[i].address = INVALID_NUB_ADDRESS; + } + // Add all remaining previous_dylibs to the changed list with + // invalidated addresses so we know they got unloaded. + m_changed_dylibs.insert(m_changed_dylibs.end(), previous_dylibs.begin(), previous_dylibs.end()); + } + } + } + return true; + } + return false; +} + + +void +MachDYLD::UpdateUUIDs() +{ + bool swap = false; + nub_size_t page_size = 0x1000; + uint32_t i; + // Read any UUID values that we can get + for (i = 0; i < m_dylib_info_header.dylib_info_count; i++) + { + if (!m_current_dylibs[i].UUIDValid()) + { + std::vector bytes(page_size, 0); + nub_size_t bytes_read = DNBProcessMemoryRead(m_pid, m_current_dylibs[i].address, page_size, &bytes[0]); + if (bytes_read > 0) + { + DNBDataRef::offset_t offset = 0; + DNBDataRef data(&bytes[0], bytes_read, swap); + struct mach_header *header = (struct mach_header*)data.GetData(&offset, sizeof(struct mach_header)); + if (header) + { + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetPointerSize(4); + m_addr_size = 4; + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetPointerSize(8); + m_addr_size = 8; + offset += 4; // Skip the extra reserved field in the 64 bit mach header + break; + + default: + continue; + } + + if (header->sizeofcmds > bytes_read) + { + bytes.resize(header->sizeofcmds); + nub_addr_t addr = m_current_dylibs[i].address + bytes_read; + bytes_read += DNBProcessMemoryRead(m_pid, addr , header->sizeofcmds - bytes_read, &bytes[bytes_read]); + } + assert(bytes_read >= header->sizeofcmds); + uint32_t cmd_idx; + DNBSegment segment; + + for (cmd_idx = 0; cmd_idx < header->ncmds; cmd_idx++) + { + if (data.ValidOffsetForDataOfSize(offset, sizeof(struct load_command))) + { + struct load_command load_cmd; + DNBDataRef::offset_t load_cmd_offset = offset; + load_cmd.cmd = data.Get32(&offset); + load_cmd.cmdsize = data.Get32(&offset); + switch (load_cmd.cmd) + { + case LC_SEGMENT: + { + strncpy(segment.name, data.GetCStr(&offset, 16), 16); + memset(&segment.name[16], 0, DNB_MAX_SEGMENT_NAME_LENGTH - 16); + segment.addr = data.Get32(&offset); + segment.size = data.Get32(&offset); + m_current_dylibs[i].segments.push_back(segment); + } + break; + + case LC_SEGMENT_64: + { + strncpy(segment.name, data.GetCStr(&offset, 16), 16); + memset(&segment.name[16], 0, DNB_MAX_SEGMENT_NAME_LENGTH - 16); + segment.addr = data.Get64(&offset); + segment.size = data.Get64(&offset); + m_current_dylibs[i].segments.push_back(segment); + } + break; + + case LC_UUID: + // We found our UUID, we can stop now... + memcpy(m_current_dylibs[i].uuid, data.GetData(&offset, 16), 16); + // if (DNBLogCheckLogBit(LOG_SHLIB)) + // { + // DNBLogThreaded("UUID found for aii[%d]:", i); + // m_current_dylibs[i].Dump(DNBLogGetLogFile()); + // } + break; + + default: + break; + } + // Set offset to be the beginning of the next load command. + offset = load_cmd_offset + load_cmd.cmdsize; + } + } + } + } + } + } +} + + +nub_addr_t +MachDYLD::GetSharedLibraryHeaderAddress(const char *shlib_path) const +{ + if (!m_current_dylibs.empty() && shlib_path && shlib_path[0]) + { + uint32_t i; + for (i = 0; iCopyChangedShlibInfo(image_infos); + else + return dyld->CopyCurrentShlibInfo(image_infos); + + *image_infos = NULL; + return 0; +} + +nub_size_t +MachDYLD::CopyCurrentShlibInfo(DNBExecutableImageInfo **image_infos) +{ + PThreadMutex::Locker locker(m_dyld_info_mutex); + return CopySharedLibraryInfo(m_current_dylibs, image_infos); +} + + +nub_size_t +MachDYLD::CopyChangedShlibInfo(DNBExecutableImageInfo **image_infos) +{ + PThreadMutex::Locker locker(m_dyld_info_mutex); + return CopySharedLibraryInfo(m_changed_dylibs, image_infos); +} + + + +void +MachDYLD::DYLIBInfo::Dump(FILE *f) const +{ + if (f == NULL) + return; + if (address == INVALID_NUB_ADDRESS) + { + if (UUIDValid()) + { + fprintf(f, "UNLOADED %8.8llx %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 %s", + (uint64_t)mod_date, + uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3], + uuid[ 4], uuid[ 5], uuid[ 6], uuid[ 7], + uuid[ 8], uuid[ 9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15], + path.c_str()); + } + else + { + fprintf(f, "UNLOADED %8.8llx %s", (uint64_t)mod_date, path.c_str()); + } + } + else + { + if (UUIDValid()) + { + fprintf(f, "%8.8llx %8.8llx %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 %s", + (uint64_t)address, + (uint64_t)mod_date, + uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3], + uuid[ 4], uuid[ 5], uuid[ 6], uuid[ 7], + uuid[ 8], uuid[ 9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15], + path.c_str()); + } + else + { + fprintf(f, "%8.8llx %8.8llx %s", (uint64_t)address, (uint64_t)mod_date, path.c_str()); + } + } +} + +void +MachDYLD::Dump(FILE *f) const +{ + if (f == NULL) + return; + + PThreadMutex::Locker locker(m_dyld_info_mutex); + fprintf(f, "\n\tMachDYLD.m_dylib_info_header: version=%d, count=%d, addr=0x%llx, notify=0x%llx", + m_dylib_info_header.version, + m_dylib_info_header.dylib_info_count, + (uint64_t)m_dylib_info_header.dylib_info_addr, + (uint64_t)m_dylib_info_header.notification); + uint32_t i; + fprintf(f, "\n\tMachDYLD.m_current_dylibs"); + for (i = 0; i +#include +#include + +class DNBBreakpoint; +class MachProcess; + +class MachDYLD : public DNBRuntimeAction +{ +public: + MachDYLD (); + virtual ~MachDYLD (); + + //------------------------------------------------------------------ + // DNBRuntimeAction required functions + //------------------------------------------------------------------ + virtual void Initialize(nub_process_t pid); + virtual void ProcessStateChanged(nub_state_t state); + virtual void SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos); + +protected: + bool CheckForDYLDInMemory(); + bool FoundDYLD() const; + void Clear(); + void Dump(FILE *f) const; + nub_process_t ProcessID() const { return m_pid; } + uint32_t AddrByteSize() const { return m_addr_size; } + nub_size_t CopyCurrentShlibInfo(DNBExecutableImageInfo **image_infos); + nub_size_t CopyChangedShlibInfo(DNBExecutableImageInfo **image_infos); + nub_addr_t GetSharedLibraryHeaderAddress(const char *shlib_path) const; + bool CheckForDYLDInMemory(nub_addr_t addr); + bool ReadDYLIBInfo (); + static nub_bool_t BreakpointHit (nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton); + void UpdateUUIDs(); + + struct DYLIBInfo + { + nub_addr_t address; // Address of mach header for this dylib + nub_addr_t mod_date; // Modification date for this dylib + std::string path; // Resolved path for this dylib + uint8_t uuid[16]; // UUID for this dylib if it has one, else all zeros + std::vector segments; // All segment vmaddr and vmsize pairs for this executable (from memory of inferior) + + DYLIBInfo() : + address(INVALID_NUB_ADDRESS), + mod_date(0), + path(), + segments() + { + memset(uuid, 0, 16); + } + + void Clear() + { + address = INVALID_NUB_ADDRESS; + mod_date = 0; + path.clear(); + segments.clear(); + memset(uuid, 0, 16); + } + + bool operator == (const DYLIBInfo& rhs) const + { + return address == rhs.address + && mod_date == rhs.mod_date + && path == rhs.path + && memcmp(uuid, rhs.uuid, 16) == 0; + } + bool UUIDValid() const + { + return uuid[ 0] || uuid[ 1] || uuid[ 2] || uuid[ 3] || + uuid[ 4] || uuid[ 5] || uuid[ 6] || uuid[ 7] || + uuid[ 8] || uuid[ 9] || uuid[10] || uuid[11] || + uuid[12] || uuid[13] || uuid[14] || uuid[15]; + } + + void Dump(FILE *f) const; + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + struct InfoHeader + { + uint32_t version; /* == 1 in Mac OS X 10.4, == 2 in Mac OS 10.5 */ + uint32_t dylib_info_count; + nub_addr_t dylib_info_addr; + nub_addr_t notification; + bool processDetachedFromSharedRegion; + + InfoHeader() : + version(0), + dylib_info_count(0), + dylib_info_addr(INVALID_NUB_ADDRESS), + notification(INVALID_NUB_ADDRESS), + processDetachedFromSharedRegion(false) + { + } + + void Clear() + { + version = 0; + dylib_info_count = 0; + dylib_info_addr = INVALID_NUB_ADDRESS; + notification = INVALID_NUB_ADDRESS; + processDetachedFromSharedRegion = false; + } + + bool IsValid() const + { + return version == 1 || version == 2; + } + }; + static nub_size_t CopySharedLibraryInfo(DYLIBInfo::collection& dylib_coll, DNBExecutableImageInfo **image_infos); + static nub_size_t CopySharedInfoCallback(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton); + nub_process_t m_pid; + uint32_t m_addr_size; + nub_addr_t m_dyld_addr; + nub_addr_t m_dyld_all_image_infos_addr; + InfoHeader m_dylib_info_header; + DYLIBInfo::collection m_current_dylibs; // Current shared libraries information + DYLIBInfo::collection m_changed_dylibs; // Shared libraries that changed since last shared library update + nub_break_t m_notify_break_id; + mutable PThreadMutex m_dyld_info_mutex; +}; + +#endif // #ifndef __MachDYLD_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.cpp b/lldb/tools/debugserver/source/MacOSX/MachException.cpp new file mode 100644 index 000000000000..356160bb6219 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachException.cpp @@ -0,0 +1,533 @@ +//===-- MachException.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#include "MachException.h" +#include "MachProcess.h" +#include "DNB.h" +#include "DNBError.h" +#include +#include "DNBLog.h" +#include "PThreadMutex.h" +#include "SysSignal.h" +#include +#include + +// Routine mach_exception_raise +extern "C" +kern_return_t catch_mach_exception_raise +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt +); + +extern "C" +kern_return_t catch_mach_exception_raise_state +( + mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +// Routine mach_exception_raise_state_identity +extern "C" +kern_return_t catch_mach_exception_raise_state_identity +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +extern "C" boolean_t mach_exc_server( + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + +// Any access to the g_message variable should be done by locking the +// g_message_mutex first, using the g_message variable, then unlocking +// the g_message_mutex. See MachException::Message::CatchExceptionRaise() +// for sample code. + +static MachException::Data *g_message = NULL; +//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER; + + +extern "C" +kern_return_t +catch_mach_exception_raise_state +( + mach_port_t exc_port, + exception_type_t exc_type, + const mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt +) +{ + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + { + DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)", + __FUNCTION__, + exc_port, + exc_type, MachException::Name(exc_type), + exc_data, + exc_data_count); + } + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise_state_identity +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +) +{ + kern_return_t kret; + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + { + DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, + exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); + } + kret = mach_port_deallocate (mach_task_self (), task_port); + kret = mach_port_deallocate (mach_task_self (), thread_port); + + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count) +{ + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + { + DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, + exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); + } + + g_message->task_port = task_port; + g_message->thread_port = thread_port; + g_message->exc_type = exc_type; + g_message->exc_data.resize(exc_data_count); + ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t)); + return KERN_SUCCESS; +} + + +void +MachException::Message::Dump() const +{ + DNBLogThreadedIf(LOG_EXCEPTIONS, + " exc_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx } ", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id); + + DNBLogThreadedIf(LOG_EXCEPTIONS, + "reply_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx }", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id); + + state.Dump(); +} + +bool +MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const +{ + // Zero out the structure. + memset(stop_info, 0, sizeof(struct DNBThreadStopInfo)); + // We always stop with a mach exceptions + stop_info->reason = eStopTypeException; + // Save the EXC_XXXX exception type + stop_info->details.exception.type = exc_type; + + // Fill in a text description + const char * exc_name = MachException::Name(exc_type); + char *desc = stop_info->description; + const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH; + if (exc_name) + desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name); + else + desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type); + + stop_info->details.exception.data_count = exc_data.size(); + + int soft_signal = SoftSignal(); + if (soft_signal) + { + if (desc < end_desc) + { + const char *sig_str = SysSignal::Name(soft_signal); + desc += snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, sig_str ? sig_str : "unknown signal"); + } + } + else + { + // No special disassembly for exception data, just + size_t idx; + if (desc < end_desc) + { + desc += snprintf(desc, end_desc - desc, " data[%zu] = {", stop_info->details.exception.data_count); + + for (idx = 0; desc < end_desc && idx < stop_info->details.exception.data_count; ++idx) + desc += snprintf(desc, end_desc - desc, MACH_EXCEPTION_DATA_FMT_MINHEX "%c", exc_data[idx], ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ',')); + } + } + + // Copy the exception data + size_t i; + for (i=0; idetails.exception.data_count; i++) + stop_info->details.exception.data[i] = exc_data[i]; + + return true; +} + + +void +MachException::Data::DumpStopReason() const +{ + int soft_signal = SoftSignal(); + if (soft_signal) + { + const char *signal_str = SysSignal::Name(soft_signal); + if (signal_str) + DNBLog("signal(%s)", signal_str); + else + DNBLog("signal(%i)", soft_signal); + return; + } + DNBLog("%s", Name(exc_type)); +} + +kern_return_t +MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port) +{ + DNBError err; + const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS); + mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0; + if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) + { + // Dump this log message if we have no timeout in case it never returns + DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + 0, + sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + + err = ::mach_msg (&exc_msg.hdr, + options, // options + 0, // Send size + sizeof (exc_msg.data), // Receive size + port, // exception port to watch for exception on + mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter) + notify_port); + + // Dump any errors we get + if (log_exceptions) + { + err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + 0, + sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + return err.Error(); +} + +bool +MachException::Message::CatchExceptionRaise() +{ + bool success = false; + // locker will keep a mutex locked until it goes out of scope +// PThreadMutex::Locker locker(&g_message_mutex); + // DNBLogThreaded("calling mach_exc_server"); + g_message = &state; + // The exc_server function is the MIG generated server handling function + // to handle messages from the kernel relating to the occurrence of an + // exception in a thread. Such messages are delivered to the exception port + // set via thread_set_exception_ports or task_set_exception_ports. When an + // exception occurs in a thread, the thread sends an exception message to + // its exception port, blocking in the kernel waiting for the receipt of a + // reply. The exc_server function performs all necessary argument handling + // for this kernel message and calls catch_exception_raise, + // catch_exception_raise_state or catch_exception_raise_state_identity, + // which should handle the exception. If the called routine returns + // KERN_SUCCESS, a reply message will be sent, allowing the thread to + // continue from the point of the exception; otherwise, no reply message + // is sent and the called routine must have dealt with the exception + // thread directly. + if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr)) + { + success = true; + } + else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + { + DNBLogThreaded("mach_exc_server returned zero..."); + } + g_message = NULL; + return success; +} + + + +kern_return_t +MachException::Message::Reply(MachProcess *process, int signal) +{ + // Reply to the exception... + DNBError err; + + // If we had a soft signal, we need to update the thread first so it can + // continue without signaling + int soft_signal = state.SoftSignal(); + if (soft_signal) + { + int state_pid = -1; + if (process->Task().TaskPort() == state.task_port) + { + // This is our task, so we can update the signal to send to it + state_pid = process->ProcessID(); + soft_signal = signal; + } + else + { + err = ::pid_for_task(state.task_port, &state_pid); + } + + assert (state_pid != -1); + if (state_pid != -1) + { + errno = 0; + if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, soft_signal) != 0) + err.SetError(errno, DNBError::POSIX); + else + err.Clear(); + + if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) + err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, soft_signal); + } + } + + DNBLogThreadedIf(LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id, + MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply_msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + err = ::mach_msg ( &reply_msg.hdr, + MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply_msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (err.Fail()) + { + if (err.Error() == MACH_SEND_INTERRUPTED) + { + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + err.LogThreaded("::mach_msg() - send interrupted"); + // TODO: keep retrying to reply??? + } + else + { + if (state.task_port == process->Task().TaskPort()) + { + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + err.LogThreaded("::mach_msg() - failed (task)"); + abort (); + } + else + { + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + err.LogThreaded("::mach_msg() - failed (child of task)"); + } + } + } + + return err.Error(); +} + + +void +MachException::Data::Dump() const +{ + const char *exc_type_name = MachException::Name(exc_type); + DNBLogThreadedIf(LOG_EXCEPTIONS, " state { task_port = 0x%4.4x, thread_port = 0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???"); + + const size_t exc_data_count = exc_data.size(); + // Dump any special exception data contents + int soft_signal = SoftSignal(); + if (soft_signal != 0) + { + const char *sig_str = SysSignal::Name(soft_signal); + DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal"); + } + else + { + // No special disassembly for this data, just dump the data + size_t idx; + for (idx = 0; idx < exc_data_count; ++idx) + { + DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%u]: " MACH_EXCEPTION_DATA_FMT_HEX, idx, exc_data[idx]); + } + } +} + + +kern_return_t +MachException::PortInfo::Save (task_t task) +{ + count = (sizeof (ports) / sizeof (ports[0])); + DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task); + DNBError err; + err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors); + if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) + err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, EXC_MASK_ALL, count); + if (err.Fail()) + count = 0; + return err.Error(); +} + +kern_return_t +MachException::PortInfo::Restore (task_t task) +{ + DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Restore( task = 0x%4.4x )", task); + uint32_t i = 0; + DNBError err; + if (count > 0) + { + for (i = 0; i < count; i++) + { + err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]); + if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) + { + err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]); + // Bail if we encounter any errors + } + + if (err.Fail()) + break; + } + } + count = 0; + return err.Error(); +} + +const char * +MachException::Name(exception_type_t exc_type) +{ + switch (exc_type) + { + case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS"; + case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION"; + case EXC_ARITHMETIC: return "EXC_ARITHMETIC"; + case EXC_EMULATION: return "EXC_EMULATION"; + case EXC_SOFTWARE: return "EXC_SOFTWARE"; + case EXC_BREAKPOINT: return "EXC_BREAKPOINT"; + case EXC_SYSCALL: return "EXC_SYSCALL"; + case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL"; + case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; +#ifdef EXC_CRASH + case EXC_CRASH: return "EXC_CRASH"; +#endif + default: + break; + } + return NULL; +} + + + diff --git a/lldb/tools/debugserver/source/MacOSX/MachException.h b/lldb/tools/debugserver/source/MacOSX/MachException.h new file mode 100644 index 000000000000..5dc394bd55ab --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachException.h @@ -0,0 +1,147 @@ +//===-- MachException.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + + +#ifndef __MachException_h__ +#define __MachException_h__ + +#include +#include +#include "DNBConfig.h" + +#ifdef HAVE_64_BIT_MACH_EXCEPTIONS + +#define MACH_EXCEPTION_DATA_FMT_DEC "%lld" +#define MACH_EXCEPTION_DATA_FMT_HEX "0x%16.16llx" +#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%llx" + +#else + +#define MACH_EXCEPTION_DATA_FMT_DEC "%d" +#define MACH_EXCEPTION_DATA_FMT_HEX "0x%8.8x" +#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%x" + +#endif + +class MachProcess; +class PThreadMutex; + +typedef union MachMessageTag +{ + mach_msg_header_t hdr; + char data[1024]; +} MachMessage; + + +class MachException +{ +public: + + struct PortInfo + { + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; + mach_msg_type_number_t count; + + kern_return_t Save(task_t task); + kern_return_t Restore(task_t task); + }; + + struct Data + { + task_t task_port; + thread_t thread_port; + exception_type_t exc_type; + std::vector exc_data; + Data() : + task_port(TASK_NULL), + thread_port(THREAD_NULL), + exc_type(0), + exc_data() + { + } + + void Clear() + { + task_port = TASK_NULL; + thread_port = THREAD_NULL; + exc_type = 0; + exc_data.clear(); + } + bool IsValid() const + { + return task_port != TASK_NULL && + thread_port != THREAD_NULL && + exc_type != 0; + } + // Return the SoftSignal for this MachException data, or zero if there is none + int SoftSignal() const + { + if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL) + return exc_data[1]; + return 0; + } + bool IsBreakpoint() const + { + return (exc_type == EXC_BREAKPOINT) || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1); + } + void Dump() const; + void DumpStopReason() const; + bool GetStopInfo(struct DNBThreadStopInfo *stop_info) const; + }; + + struct Message + { + MachMessage exc_msg; + MachMessage reply_msg; + Data state; + + Message() : + state() + { + memset(&exc_msg, 0, sizeof(exc_msg)); + memset(&reply_msg, 0, sizeof(reply_msg)); + } + bool CatchExceptionRaise(); + void Dump() const; + kern_return_t Reply (MachProcess *process, int signal); + kern_return_t Receive( mach_port_t receive_port, + mach_msg_option_t options, + mach_msg_timeout_t timeout, + mach_port_t notify_port = MACH_PORT_NULL); + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + enum + { + e_actionForward, // Forward signal to inferior process + e_actionStop, // Stop when this signal is received + }; + struct Action + { + task_t task_port; // Set to TASK_NULL for any TASK + thread_t thread_port; // Set to THREAD_NULL for any thread + exception_type_t exc_mask; // Mach exception mask to watch for + std::vector exc_data_mask; // Mask to apply to exception data, or empty to ignore exc_data value for exception + std::vector exc_data_value; // Value to compare to exception data after masking, or empty to ignore exc_data value for exception + uint8_t flags; // Action flags describing what to do with the exception + }; + static const char *Name(exception_type_t exc_type); +}; + +#endif diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp new file mode 100644 index 000000000000..2bfc7603e2af --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp @@ -0,0 +1,2008 @@ +//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/15/07. +// +//===----------------------------------------------------------------------===// + +#include "DNB.h" +#include +#include +#include +#include +#include +#include +#include +#include "MacOSX/CFUtils.h" +#include "SysSignal.h" + +#include +#include + +#include "DNBDataRef.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "DNBTimer.h" +#include "MachProcess.h" +#include "PseudoTerminal.h" + +#include "CFBundle.h" +#include "CFData.h" +#include "CFString.h" + +static CFStringRef CopyBundleIDForPath (const char *app_buncle_path, DNBError &err_str); + +#if defined (__arm__) + +#include +#include +#include + + +static bool +IsSBProcess (nub_process_t pid) +{ + bool opt_runningApps = true; + bool opt_debuggable = false; + + CFReleaser sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); + if (sbsAppIDs.get() != NULL) + { + CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); + CFIndex i = 0; + for (i = 0; i < count; i++) + { + CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); + + // Get the process id for the app (if there is one) + pid_t sbs_pid = INVALID_NUB_PROCESS; + if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) + { + if (sbs_pid == pid) + return true; + } + } + } + return false; +} + + +#endif + +#if 0 +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +#ifndef MACH_PROCESS_USE_POSIX_SPAWN +#define MACH_PROCESS_USE_POSIX_SPAWN 1 +#endif + + +MachProcess::MachProcess() : + m_pid (0), + m_child_stdin (-1), + m_child_stdout (-1), + m_child_stderr (-1), + m_path (), + m_args (), + m_task (this), + m_flags (eMachProcessFlagsNone), + m_stdio_thread (0), + m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE), + m_stdout_data (), + m_threadList (), + m_exception_messages (), + m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), + m_err (KERN_SUCCESS), + m_state (eStateUnloaded), + m_state_mutex (PTHREAD_MUTEX_RECURSIVE), + m_events (0, kAllEventsMask), + m_breakpoints (), + m_watchpoints (), + m_name_to_addr_callback(NULL), + m_name_to_addr_baton(NULL), + m_image_infos_callback(NULL), + m_image_infos_baton(NULL) +{ + DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); +} + +MachProcess::~MachProcess() +{ + DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); + Clear(); +} + +pid_t +MachProcess::SetProcessID(pid_t pid) +{ + // Free any previous process specific data or resources + Clear(); + // Set the current PID appropriately + if (pid == 0) + m_pid == ::getpid (); + else + m_pid = pid; + return m_pid; // Return actualy PID in case a zero pid was passed in +} + +nub_state_t +MachProcess::GetState() +{ + // If any other threads access this we will need a mutex for it + PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); + return m_state; +} + +const char * +MachProcess::ThreadGetName(nub_thread_t tid) +{ + return m_threadList.GetName(tid); +} + +nub_state_t +MachProcess::ThreadGetState(nub_thread_t tid) +{ + return m_threadList.GetState(tid); +} + + +nub_size_t +MachProcess::GetNumThreads () const +{ + return m_threadList.NumThreads(); +} + +nub_thread_t +MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const +{ + return m_threadList.ThreadIDAtIndex(thread_idx); +} + +uint32_t +MachProcess::GetThreadIndexFromThreadID (nub_thread_t tid) +{ + return m_threadList.GetThreadIndexByID(tid); +} + +nub_thread_t +MachProcess::GetCurrentThread () +{ + return m_threadList.CurrentThreadID(); +} + +nub_thread_t +MachProcess::SetCurrentThread(nub_thread_t tid) +{ + return m_threadList.SetCurrentThread(tid); +} + +bool +MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const +{ + return m_threadList.GetThreadStoppedReason(tid, stop_info); +} + +void +MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const +{ + return m_threadList.DumpThreadStoppedReason(tid); +} + +const char * +MachProcess::GetThreadInfo(nub_thread_t tid) const +{ + return m_threadList.GetThreadInfo(tid); +} + +const DNBRegisterSetInfo * +MachProcess::GetRegisterSetInfo(nub_thread_t tid, nub_size_t *num_reg_sets ) const +{ + return DNBArch::GetRegisterSetInfo (num_reg_sets); +} + +bool +MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const +{ + return m_threadList.GetRegisterValue(tid, set, reg, value); +} + +bool +MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const +{ + return m_threadList.SetRegisterValue(tid, set, reg, value); +} + +void +MachProcess::SetState(nub_state_t new_state) +{ + // If any other threads access this we will need a mutex for it + uint32_t event_mask = 0; + + // Scope for mutex locker + { + PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state)); + + const nub_state_t old_state = m_state; + + if (old_state != new_state) + { + if (NUB_STATE_IS_STOPPED(new_state)) + event_mask = eEventProcessStoppedStateChanged; + else + event_mask = eEventProcessRunningStateChanged; + + m_state = new_state; + if (new_state == eStateStopped) + m_stop_count++; + } + } + + if (event_mask != 0) + { + m_events.SetEvents (event_mask); + + // Wait for the event bit to reset if a reset ACK is requested + m_events.WaitForResetAck(event_mask); + } + +} + +void +MachProcess::Clear() +{ + // Clear any cached thread list while the pid and task are still valid + + m_task.Clear(); + // Now clear out all member variables + m_pid = INVALID_NUB_PROCESS; + CloseChildFileDescriptors(); + m_path.clear(); + m_args.clear(); + SetState(eStateUnloaded); + m_flags = eMachProcessFlagsNone; + m_stop_count = 0; + m_threadList.Clear(); + { + PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); + m_exception_messages.clear(); + } + m_err.Clear(); + +} + + +bool +MachProcess::StartSTDIOThread() +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); + // Create the thread that watches for the child STDIO + m_err = ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this); + return m_err.Success(); +} + + +nub_addr_t +MachProcess::LookupSymbol(const char *name, const char *shlib) +{ + if (m_name_to_addr_callback != NULL && name && name[0]) + return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton); + return INVALID_NUB_ADDRESS; +} + +bool +MachProcess::Resume (const DNBThreadResumeActions& thread_actions) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()"); + nub_state_t state = GetState(); + + if (CanResume(state)) + { + PrivateResume(thread_actions); + } + else if (state == eStateRunning) + { + DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort()); + m_err.Clear(); + + } + else + { + DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort()); + m_err.SetError(UINT_MAX, DNBError::Generic); + } + return m_err.Success(); +} + +bool +MachProcess::Kill (const struct timespec *timeout_abstime) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); + nub_state_t state = DoSIGSTOP(true); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state)); + errno = 0; + ::ptrace (PT_KILL, m_pid, 0, 0); + m_err.SetErrorToErrno(); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_err.Error(), m_err.AsString()); + PrivateResume (DNBThreadResumeActions (eStateRunning, 0)); + return true; +} + +bool +MachProcess::Signal (int signal, const struct timespec *timeout_abstime) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime); + nub_state_t state = GetState(); + if (::kill (ProcessID(), signal) == 0) + { + m_err.Clear(); + // If we were running and we have a timeout, wait for the signal to stop + if (IsRunning(state) && timeout_abstime) + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime); + m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); + state = GetState(); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state)); + return !IsRunning (state); + } + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime); + } + else + { + m_err.SetError(errno, DNBError::POSIX); + m_err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); + } + return m_err.Success(); + +} + +nub_state_t +MachProcess::DoSIGSTOP (bool clear_bps_and_wps) +{ + nub_state_t state = GetState(); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state)); + + if (!IsRunning(state)) + { + if (clear_bps_and_wps) + { + DisableAllBreakpoints (true); + DisableAllWatchpoints (true); + clear_bps_and_wps = false; + } + + // Resume our process + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state)); + PrivateResume (DNBThreadResumeActions (eStateRunning, 0)); + + // Reset the event that says we were indeed running + m_events.ResetEvents(eEventProcessRunningStateChanged); + state = GetState(); + } + + // We need to be stopped in order to be able to detach, so we need + // to send ourselves a SIGSTOP + + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state)); + struct timespec sigstop_timeout; + DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0); + Signal (SIGSTOP, &sigstop_timeout); + if (clear_bps_and_wps) + { + DisableAllBreakpoints (true); + DisableAllWatchpoints (true); + clear_bps_and_wps = false; + } + return GetState(); +} + +bool +MachProcess::Detach() +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); + + nub_state_t state = DoSIGSTOP(true); + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state)); + + + // Don't reply to our SIGSTOP exception, just make sure no threads + // are still suspended. + PrivateResume (DNBThreadResumeActions (eStateRunning, 0)); + + + // Detach from our process + errno = 0; + nub_process_t pid = m_pid; + ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + m_err.SetError (errno, DNBError::POSIX); + if (DNBLogCheckLogBit(LOG_PROCESS) || m_err.Fail()) + m_err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + + // Resume our task + m_task.Resume(); + + SetState(eStateDetached); + + // NULL our task out as we have already retored all exception ports + m_task.Clear(); + + // Clear out any notion of the process we once were + Clear(); + return true; +} + + +nub_size_t +MachProcess::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const +{ + nub_size_t bytes_removed = 0; + const DNBBreakpoint *bp; + nub_addr_t intersect_addr; + nub_size_t intersect_size; + nub_size_t opcode_offset; + nub_size_t idx; + for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx) + { + if (bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)) + { + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->ByteSize()); + nub_size_t buf_offset = intersect_addr - addr; + ::memcpy(buf + buf_offset, bp->SavedOpcodeBytes() + opcode_offset, intersect_size); + } + } + return bytes_removed; +} + +//---------------------------------------------------------------------- +// ReadMemory from the MachProcess level will always remove any software +// breakpoints from the memory buffer before returning. If you wish to +// read memory and see those traps, read from the MachTask +// (m_task.ReadMemory()) as that version will give you what is actually +// in inferior memory. +//---------------------------------------------------------------------- +nub_size_t +MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf) +{ + // We need to remove any current software traps (enabled software + // breakpoints) that we may have placed in our tasks memory. + + // First just read the memory as is + nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf); + + // Then place any opcodes that fall into this range back into the buffer + // before we return this to callers. + if (bytes_read > 0) + RemoveTrapsFromBuffer (addr, size, (uint8_t *)buf); + return bytes_read; +} + +//---------------------------------------------------------------------- +// WriteMemory from the MachProcess level will always write memory around +// any software breakpoints. Any software breakpoints will have their +// opcodes modified if they are enabled. Any memory that doesn't overlap +// with software breakpoints will be written to. If you wish to write to +// inferior memory without this interference, then write to the MachTask +// (m_task.WriteMemory()) as that version will always modify inferior +// memory. +//---------------------------------------------------------------------- +nub_size_t +MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) +{ + // We need to write any data that would go where any current software traps + // (enabled software breakpoints) any software traps (breakpoints) that we + // may have placed in our tasks memory. + + std::map addr_to_bp_map; + DNBBreakpoint *bp; + nub_size_t idx; + for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx) + { + if (bp->IntersectsRange(addr, size, NULL, NULL, NULL)) + addr_to_bp_map[bp->Address()] = bp; + } + + // If we don't have any software breakpoints that are in this buffer, then + // we can just write memory and be done with it. + if (addr_to_bp_map.empty()) + return m_task.WriteMemory(addr, size, buf); + + // If we make it here, we have some breakpoints that overlap and we need + // to work around them. + + nub_size_t bytes_written = 0; + nub_addr_t intersect_addr; + nub_size_t intersect_size; + nub_size_t opcode_offset; + const uint8_t *ubuf = (const uint8_t *)buf; + std::map::iterator pos, end = addr_to_bp_map.end(); + for (pos = addr_to_bp_map.begin(); pos != end; ++pos) + { + bp = pos->second; + + assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)); + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->ByteSize()); + + // Check for bytes before this breakpoint + const nub_addr_t curr_addr = addr + bytes_written; + if (intersect_addr > curr_addr) + { + // There are some bytes before this breakpoint that we need to + // just write to memory + nub_size_t curr_size = intersect_addr - curr_addr; + nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written); + bytes_written += curr_bytes_written; + if (curr_bytes_written != curr_size) + { + // We weren't able to write all of the requested bytes, we + // are done looping and will return the number of bytes that + // we have written so far. + break; + } + } + + // Now write any bytes that would cover up any software breakpoints + // directly into the breakpoint opcode buffer + ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); + bytes_written += intersect_size; + } + + // Write any remaining bytes after the last breakpoint if we have any left + if (bytes_written < size) + bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written); + + return bytes_written; +} + + +void +MachProcess::ReplyToAllExceptions (const DNBThreadResumeActions& thread_actions) +{ + PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); + if (m_exception_messages.empty() == false) + { + MachException::Message::iterator pos; + MachException::Message::iterator begin = m_exception_messages.begin(); + MachException::Message::iterator end = m_exception_messages.end(); + for (pos = begin; pos != end; ++pos) + { + DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %d...", std::distance(begin, pos)); + int thread_reply_signal = 0; + + const DNBThreadResumeAction *action = thread_actions.GetActionForThread (pos->state.thread_port, false); + + if (action) + { + thread_reply_signal = action->signal; + if (thread_reply_signal) + thread_actions.SetSignalHandledForThread (pos->state.thread_port); + } + + m_err = pos->Reply(this, thread_reply_signal); + if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) + m_err.LogThreadedIfError("Error replying to exception"); + } + + // Erase all exception message as we should have used and replied + // to them all already. + m_exception_messages.clear(); + } +} +void +MachProcess::PrivateResume (const DNBThreadResumeActions& thread_actions) +{ + PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); + + ReplyToAllExceptions (thread_actions); +// bool stepOverBreakInstruction = step; + + // Let the thread prepare to resume and see if any threads want us to + // step over a breakpoint instruction (ProcessWillResume will modify + // the value of stepOverBreakInstruction). + m_threadList.ProcessWillResume (this, thread_actions); + + // Set our state accordingly + if (thread_actions.NumActionsWithState(eStateStepping)) + SetState (eStateStepping); + else + SetState (eStateRunning); + + // Now resume our task. + m_err = m_task.Resume(); + +} + +nub_break_t +MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware, thread_t tid) +{ + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, hardware, tid); + if (hardware && tid == INVALID_NUB_THREAD) + tid = GetCurrentThread(); + + DNBBreakpoint bp(addr, length, tid, hardware); + nub_break_t breakID = m_breakpoints.Add(bp); + if (EnableBreakpoint(breakID)) + { + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%4.4x ) => %u", (uint64_t)addr, length, tid, breakID); + return breakID; + } + else + { + m_breakpoints.Remove(breakID); + } + // We failed to enable the breakpoint + return INVALID_NUB_BREAK_ID; +} + +nub_watch_t +MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware, thread_t tid) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, flags = 0x%8.8x, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, watch_flags, hardware, tid); + if (hardware && tid == INVALID_NUB_THREAD) + tid = GetCurrentThread(); + + DNBBreakpoint watch(addr, length, tid, hardware); + watch.SetIsWatchpoint(watch_flags); + + nub_watch_t watchID = m_watchpoints.Add(watch); + if (EnableWatchpoint(watchID)) + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => %u", (uint64_t)addr, length, tid, watchID); + return watchID; + } + else + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => FAILED (%u)", (uint64_t)addr, length, tid, watchID); + m_watchpoints.Remove(watchID); + } + // We failed to enable the watchpoint + return INVALID_NUB_BREAK_ID; +} + +nub_size_t +MachProcess::DisableAllBreakpoints(bool remove) +{ + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); + DNBBreakpoint *bp; + nub_size_t disabled_count = 0; + nub_size_t idx = 0; + while ((bp = m_breakpoints.GetByIndex(idx)) != NULL) + { + bool success = DisableBreakpoint(bp->GetID(), remove); + + if (success) + disabled_count++; + // If we failed to disable the breakpoint or we aren't removing the breakpoint + // increment the breakpoint index. Otherwise DisableBreakpoint will have removed + // the breakpoint at this index and we don't need to change it. + if ((success == false) || (remove == false)) + idx++; + } + return disabled_count; +} + +nub_size_t +MachProcess::DisableAllWatchpoints(bool remove) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); + DNBBreakpoint *wp; + nub_size_t disabled_count = 0; + nub_size_t idx = 0; + while ((wp = m_watchpoints.GetByIndex(idx)) != NULL) + { + bool success = DisableWatchpoint(wp->GetID(), remove); + + if (success) + disabled_count++; + // If we failed to disable the watchpoint or we aren't removing the watchpoint + // increment the watchpoint index. Otherwise DisableWatchpoint will have removed + // the watchpoint at this index and we don't need to change it. + if ((success == false) || (remove == false)) + idx++; + } + return disabled_count; +} + +bool +MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove) +{ + DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); + if (bp) + { + nub_addr_t addr = bp->Address(); + DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr); + + if (bp->IsHardware()) + { + bool hw_disable_result = m_threadList.DisableHardwareBreakpoint (bp); + + if (hw_disable_result == true) + { + bp->SetEnabled(false); + // Let the thread list know that a breakpoint has been modified + if (remove) + { + m_threadList.NotifyBreakpointChanged(bp); + m_breakpoints.Remove(breakID); + } + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", breakID, remove, (uint64_t)addr); + return true; + } + + return false; + } + + const nub_size_t break_op_size = bp->ByteSize(); + assert (break_op_size > 0); + const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(bp->ByteSize()); + if (break_op_size > 0) + { + // Clear a software breakoint instruction + uint8_t curr_break_op[break_op_size]; + bool break_op_found = false; + + // Read the breakpoint opcode + if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size) + { + bool verify = false; + if (bp->IsEnabled()) + { + // Make sure we have the a breakpoint opcode exists at this address + if (memcmp(curr_break_op, break_op, break_op_size) == 0) + { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) + { + verify = true; + } + else + { + DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx memory write failed when restoring original opcode", breakID, remove, (uint64_t)addr); + } + } + else + { + DNBLogWarning("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx expected a breakpoint opcode but didn't find one.", breakID, remove, (uint64_t)addr); + // Set verify to true and so we can check if the original opcode has already been restored + verify = true; + } + } + else + { + DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x$8.8llx is not enabled", breakID, remove, (uint64_t)addr); + // Set verify to true and so we can check if the original opcode is there + verify = true; + } + + if (verify) + { + uint8_t verify_opcode[break_op_size]; + // Verify that our original opcode made it back to the inferior + if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size) + { + // compare the memory we just read with the original opcode + if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0) + { + // SUCCESS + bp->SetEnabled(false); + // Let the thread list know that a breakpoint has been modified + if (remove) + { + m_threadList.NotifyBreakpointChanged(bp); + m_breakpoints.Remove(breakID); + } + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx => success", breakID, remove, (uint64_t)addr); + return true; + } + else + { + if (break_op_found) + DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: failed to restore original opcode", breakID, remove, (uint64_t)addr); + else + DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: opcode changed", breakID, remove, (uint64_t)addr); + } + } + else + { + DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr); + } + } + } + else + { + DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr); + } + } + } + else + { + DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) invalid breakpoint ID", breakID, remove); + } + return false; +} + +bool +MachProcess::DisableWatchpoint(nub_watch_t watchID, bool remove) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(watchID = %d, remove = %d)", __FUNCTION__, watchID, remove); + DNBBreakpoint *wp = m_watchpoints.FindByID (watchID); + if (wp) + { + nub_addr_t addr = wp->Address(); + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx", watchID, remove, (uint64_t)addr); + + if (wp->IsHardware()) + { + bool hw_disable_result = m_threadList.DisableHardwareWatchpoint (wp); + + if (hw_disable_result == true) + { + wp->SetEnabled(false); + if (remove) + m_watchpoints.Remove(watchID); + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", watchID, remove, (uint64_t)addr); + return true; + } + } + + // TODO: clear software watchpoints if we implement them + } + else + { + DNBLogError("MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) invalid watchpoint ID", watchID, remove); + } + return false; +} + + +void +MachProcess::DumpBreakpoint(nub_break_t breakID) const +{ + DNBLogThreaded("MachProcess::DumpBreakpoint(breakID = %d)", breakID); + + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + const DNBBreakpoint *bp = m_breakpoints.FindByID(breakID); + if (bp) + bp->Dump(); + else + DNBLog("MachProcess::DumpBreakpoint(breakID = %d): invalid breakID", breakID); + } + else + { + m_breakpoints.Dump(); + } +} + +void +MachProcess::DumpWatchpoint(nub_watch_t watchID) const +{ + DNBLogThreaded("MachProcess::DumpWatchpoint(watchID = %d)", watchID); + + if (NUB_BREAK_ID_IS_VALID(watchID)) + { + const DNBBreakpoint *wp = m_watchpoints.FindByID(watchID); + if (wp) + wp->Dump(); + else + DNBLog("MachProcess::DumpWatchpoint(watchID = %d): invalid watchID", watchID); + } + else + { + m_watchpoints.Dump(); + } +} + +bool +MachProcess::EnableBreakpoint(nub_break_t breakID) +{ + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d )", breakID); + DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); + if (bp) + { + nub_addr_t addr = bp->Address(); + if (bp->IsEnabled()) + { + DNBLogWarning("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint already enabled.", breakID, (uint64_t)addr); + return true; + } + else + { + if (bp->HardwarePreferred()) + { + bp->SetHardwareIndex(m_threadList.EnableHardwareBreakpoint(bp)); + if (bp->IsHardware()) + { + bp->SetEnabled(true); + return true; + } + } + + const nub_size_t break_op_size = bp->ByteSize(); + assert (break_op_size != 0); + const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(break_op_size); + if (break_op_size > 0) + { + // Save the original opcode by reading it + if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) + { + // Write a software breakpoint in place of the original opcode + if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size) + { + uint8_t verify_break_op[4]; + if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size) + { + if (memcmp(break_op, verify_break_op, break_op_size) == 0) + { + bp->SetEnabled(true); + // Let the thread list know that a breakpoint has been modified + m_threadList.NotifyBreakpointChanged(bp); + DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: SUCCESS.", breakID, (uint64_t)addr); + return true; + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint opcode verification failed.", breakID, (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory to verify breakpoint opcode.", breakID, (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to write breakpoint opcode to memory.", breakID, (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory at breakpoint address.", breakID, (uint64_t)addr); + } + } + else + { + DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) no software breakpoint opcode for current architecture.", breakID); + } + } + } + return false; +} + +bool +MachProcess::EnableWatchpoint(nub_watch_t watchID) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(watchID = %d)", watchID); + DNBBreakpoint *wp = m_watchpoints.FindByID (watchID); + if (wp) + { + nub_addr_t addr = wp->Address(); + if (wp->IsEnabled()) + { + DNBLogWarning("MachProcess::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return true; + } + else + { + // Currently only try and set hardware watchpoints. + wp->SetHardwareIndex(m_threadList.EnableHardwareWatchpoint(wp)); + if (wp->IsHardware()) + { + wp->SetEnabled(true); + return true; + } + // TODO: Add software watchpoints by doing page protection tricks. + } + } + return false; +} + +// Called by the exception thread when an exception has been received from +// our process. The exception message is completely filled and the exception +// data has already been copied. +void +MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage) +{ + PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); + + if (m_exception_messages.empty()) + m_task.Suspend(); + + DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )"); + + // Use a locker to automatically unlock our mutex in case of exceptions + // Add the exception to our internal exception stack + m_exception_messages.push_back(exceptionMessage); +} + +void +MachProcess::ExceptionMessageBundleComplete() +{ + // We have a complete bundle of exceptions for our child process. + PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); + DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); + if (!m_exception_messages.empty()) + { + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_threadList.ProcessDidStop(this); + + // Let each thread know of any exceptions + task_t task = m_task.TaskPort(); + size_t i; + for (i=0; i %s) ...", __FUNCTION__, len, s); + PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); + m_stdout_data.append(s, len); + m_events.SetEvents(eEventStdioAvailable); + + // Wait for the event bit to reset if a reset ACK is requested + m_events.WaitForResetAck(eEventStdioAvailable); +} + +size_t +MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size) +{ + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + } + } + return bytes_available; +} + +nub_addr_t +MachProcess::GetDYLDAllImageInfosAddress () +{ + return m_task.GetDYLDAllImageInfosAddress(m_err); +} + +size_t +MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size) +{ + return 0; +} + +void * +MachProcess::STDIOThread(void *arg) +{ + MachProcess *proc = (MachProcess*) arg; + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); + + // We start use a base and more options so we can control if we + // are currently using a timeout on the mach_msg. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main thread loop + // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + DNBError err; + int stdout_fd = proc->GetStdoutFileDescriptor(); + int stderr_fd = proc->GetStderrFileDescriptor(); + if (stdout_fd == stderr_fd) + stderr_fd = -1; + + while (stdout_fd >= 0 || stderr_fd >= 0) + { + ::pthread_testcancel (); + + fd_set read_fds; + FD_ZERO (&read_fds); + if (stdout_fd >= 0) + FD_SET (stdout_fd, &read_fds); + if (stderr_fd >= 0) + FD_SET (stderr_fd, &read_fds); + int nfds = std::max(stdout_fd, stderr_fd) + 1; + + int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); + DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + + if (num_set_fds < 0) + { + int select_errno = errno; + if (DNBLogCheckLogBit(LOG_PROCESS)) + { + err.SetError (select_errno, DNBError::POSIX); + err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + } + + switch (select_errno) + { + case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO + break; + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return NULL; + break; + case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + break; + } + } + else if (num_set_fds == 0) + { + } + else + { + char s[1024]; + s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination + int bytes_read = 0; + if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) + { + do + { + bytes_read = ::read (stdout_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); + stdout_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + + if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) + { + do + { + bytes_read = ::read (stderr_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); + stderr_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + } + } + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg); + return NULL; +} + +pid_t +MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len) +{ + // Clear out and clean up from any current state + Clear(); + if (pid != 0) + { + // Make sure the process exists... + if (::getpgid (pid) < 0) + { + m_err.SetErrorToErrno(); + const char *err_cstr = m_err.AsString(); + ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process"); + return INVALID_NUB_PROCESS; + } + + SetState(eStateAttaching); + m_pid = pid; + // Let ourselves know we are going to be using SBS if the correct flag bit is set... +#if defined (__arm__) + if (IsSBProcess(pid)) + m_flags |= eMachProcessFlagsUsingSBS; +#endif + if (!m_task.StartExceptionThread(m_err)) + { + const char *err_cstr = m_err.AsString(); + ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); + DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); + m_pid = INVALID_NUB_PROCESS; + return INVALID_NUB_PROCESS; + } + + errno = 0; + int err = ptrace (PT_ATTACHEXC, pid, 0, 0); + + if (err < 0) + m_err.SetError(errno); + else + m_err.Clear(); + + if (m_err.Success()) + { + m_flags |= eMachProcessFlagsAttached; + // Sleep a bit to let the exception get received and set our process status + // to stopped. + ::usleep(250000); + DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid); + return m_pid; + } + else + { + ::snprintf (err_str, err_len, "%s", m_err.AsString()); + DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); + } + } + return INVALID_NUB_PROCESS; +} + +// Do the process specific setup for attach. If this returns NULL, then there's no +// platform specific stuff to be done to wait for the attach. If you get non-null, +// pass that token to the CheckForProcess method, and then to CleanupAfterAttach. + +// Call PrepareForAttach before attaching to a process that has not yet launched +// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach. +// You should call CleanupAfterAttach to free the token, and do whatever other +// cleanup seems good. + +const void * +MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str) +{ +#if defined (__arm__) + // Tell SpringBoard to halt the next launch of this application on startup. + + if (!waitfor) + return NULL; + + const char *app_ext = strstr(path, ".app"); + if (app_ext == NULL) + { + DNBLogThreadedIf(LOG_PROCESS, "%s: path '%s' doesn't contain .app, we can't tell springboard to wait for launch...", path); + return NULL; + } + + if (launch_flavor != eLaunchFlavorSpringBoard + && launch_flavor != eLaunchFlavorDefault) + return NULL; + + std::string app_bundle_path(path, app_ext + strlen(".app")); + + CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), err_str); + std::string bundleIDStr; + CFString::UTF8(bundleIDCFStr, bundleIDStr); + DNBLogThreadedIf(LOG_PROCESS, "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", app_bundle_path.c_str (), bundleIDStr.c_str()); + + if (bundleIDCFStr == NULL) + { + return NULL; + } + + SBSApplicationLaunchError sbs_error = 0; + + const char *stdout_err = "/dev/null"; + CFString stdio_path; + stdio_path.SetFileSystemRepresentation (stdout_err); + + DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", bundleIDStr.c_str(), stdout_err, stdout_err); + sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, + (CFURLRef)NULL, // openURL + NULL, // launch_argv.get(), + NULL, // launch_envp.get(), // CFDictionaryRef environment + stdio_path.get(), + stdio_path.get(), + SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger); + + if (sbs_error != SBSApplicationLaunchErrorSuccess) + { + err_str.SetError(sbs_error, DNBError::SpringBoard); + return NULL; + } + + DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); + return bundleIDCFStr; +# else + return NULL; +#endif +} + +// Pass in the token you got from PrepareForAttach. If there is a process +// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS +// will be returned. + +nub_process_t +MachProcess::CheckForProcess (const void *attach_token) +{ + if (attach_token == NULL) + return INVALID_NUB_PROCESS; + +#if defined (__arm__) + CFStringRef bundleIDCFStr = (CFStringRef) attach_token; + Boolean got_it; + nub_process_t attach_pid; + got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid); + if (got_it) + return attach_pid; + else + return INVALID_NUB_PROCESS; +#endif + return INVALID_NUB_PROCESS; +} + +// Call this to clean up after you have either attached or given up on the attach. +// Pass true for success if you have attached, false if you have not. +// The token will also be freed at this point, so you can't use it after calling +// this method. + +void +MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str) +{ +#if defined (__arm__) + if (attach_token == NULL) + return; + + // Tell SpringBoard to cancel the debug on next launch of this application + // if we failed to attach + if (!success) + { + SBSApplicationLaunchError sbs_error = 0; + CFStringRef bundleIDCFStr = (CFStringRef) attach_token; + + sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, + (CFURLRef)NULL, + NULL, + NULL, + NULL, + NULL, + SBSApplicationCancelDebugOnNextLaunch); + + if (sbs_error != SBSApplicationLaunchErrorSuccess) + { + err_str.SetError(sbs_error, DNBError::SpringBoard); + return; + } + } + + CFRelease((CFStringRef) attach_token); +#endif +} + +pid_t +MachProcess::LaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + const char *stdio_path, + nub_launch_flavor_t launch_flavor, + DNBError &launch_err +) +{ + // Clear out and clean up from any current state + Clear(); + + DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u )", __FUNCTION__, path, argv, envp, launch_flavor); + + // Fork a child process for debugging + SetState(eStateLaunching); + + switch (launch_flavor) + { + case eLaunchFlavorForkExec: + m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); + break; + + case eLaunchFlavorPosixSpawn: + m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, argv, envp, stdio_path, this, launch_err); + break; + +#if defined (__arm__) + + case eLaunchFlavorSpringBoard: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext != NULL) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, launch_err); + } + } + break; + +#endif + + default: + // Invalid launch + launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); + return INVALID_NUB_PROCESS; + } + + if (m_pid == INVALID_NUB_PROCESS) + { + // If we don't have a valid process ID and no one has set the error, + // then return a generic error + if (launch_err.Success()) + launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); + } + else + { + m_path = path; + size_t i; + char const *arg; + for (i=0; (arg = argv[i]) != NULL; i++) + m_args.push_back(arg); + + m_task.StartExceptionThread(m_err); + if (m_err.Fail()) + { + if (m_err.AsString() == NULL) + m_err.SetErrorString("unable to start the exception thread"); + ::ptrace (PT_KILL, m_pid, 0, 0); + m_pid = INVALID_NUB_PROCESS; + return INVALID_NUB_PROCESS; + } + + StartSTDIOThread(); + + if (launch_flavor == eLaunchFlavorPosixSpawn) + { + + SetState (eStateAttaching); + errno = 0; + int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0); + if (err == 0) + { + m_flags |= eMachProcessFlagsAttached; + DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid); + launch_err.Clear(); + } + else + { + SetState (eStateExited); + DNBError ptrace_err(errno, DNBError::POSIX); + DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid %d (err = %i, errno = %i (%s))", m_pid, err, ptrace_err.Error(), ptrace_err.AsString()); + launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); + } + } + else + { + launch_err.Clear(); + } + } + return m_pid; +} + +pid_t +MachProcess::PosixSpawnChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + const char *stdio_path, + MachProcess* process, + DNBError& err +) +{ + posix_spawnattr_t attr; + DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); + + err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawnattr_init ( &attr )"); + if (err.Fail()) + return INVALID_NUB_PROCESS; + + err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )"); + if (err.Fail()) + return INVALID_NUB_PROCESS; + + // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail + // and we will fail to continue with our process... + + // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment.... + +//#ifndef _POSIX_SPAWN_DISABLE_ASLR +//#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 +//#endif +// err.SetError( ::posix_spawnattr_setflags (&attr, _POSIX_SPAWN_DISABLE_ASLR), DNBError::POSIX); +// if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) +// err.LogThreaded("::posix_spawnattr_setflags ( &attr, _POSIX_SPAWN_DISABLE_ASLR )"); + +#if !defined(__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu_type = DNBArch::GetCPUType(); + size_t ocount = 0; + err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu_type, ocount); + + if (err.Fail() != 0 || ocount != 1) + return INVALID_NUB_PROCESS; + +#endif + + PseudoTerminal pty; + + posix_spawn_file_actions_t file_actions; + err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX); + int file_actions_valid = err.Success(); + if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )"); + int pty_error = -1; + pid_t pid = INVALID_NUB_PROCESS; + if (file_actions_valid) + { + if (stdio_path == NULL) + { + pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); + if (pty_error == PseudoTerminal::success) + stdio_path = pty.SlaveName(); + // Make sure we were able to get the slave name + if (stdio_path == NULL) + stdio_path = "/dev/null"; + } + + if (stdio_path != NULL) + { + err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stdio_path, O_RDWR, 0), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stdio_path); + + err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdio_path, O_RDONLY, 0), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdio_path); + + err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdio_path, O_WRONLY, 0), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdio_path); + } + err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); + } + else + { + err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); + if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); + } + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (err.Fail()) + pid = INVALID_NUB_PROCESS; + + if (pty_error == 0) + { + if (process != NULL) + { + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + } + + if (file_actions_valid) + { + DNBError err2; + err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX); + if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) + err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )"); + } + + return pid; +} + +pid_t +MachProcess::ForkChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + MachProcess* process, + DNBError& launch_err +) +{ + PseudoTerminal::Error pty_error = PseudoTerminal::success; + + // Use a fork that ties the child process's stdin/out/err to a pseudo + // terminal so we can read it in our MachProcess::STDIOThread + // as unbuffered io. + PseudoTerminal pty; + pid_t pid = pty.Fork(pty_error); + + if (pid < 0) + { + //-------------------------------------------------------------- + // Error during fork. + //-------------------------------------------------------------- + return pid; + } + else if (pid == 0) + { + //-------------------------------------------------------------- + // Child process + //-------------------------------------------------------------- + ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process + ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions + + // If our parent is setgid, lets make sure we don't inherit those + // extra powers due to nepotism. + ::setgid (getgid ()); + + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (0, 0); // Set the child process group to match its pid + + // Sleep a bit to before the exec call + ::sleep (1); + + // Turn this process into + ::execv (path, (char * const *)argv); + // Exit with error code. Child process should have taken + // over in above exec call and if the exec fails it will + // exit the child process below. + ::exit (127); + } + else + { + //-------------------------------------------------------------- + // Parent process + //-------------------------------------------------------------- + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (pid, pid); // Set the child process group to match its pid + + if (process != NULL) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + } + return pid; +} + +#if defined (__arm__) + +pid_t +MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], DNBError &launch_err) +{ + // Clear out and clean up from any current state + Clear(); + + DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); + + // Fork a child process for debugging + SetState(eStateLaunching); + m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, this, launch_err); + if (m_pid != 0) + { + m_flags |= eMachProcessFlagsUsingSBS; + m_path = path; + size_t i; + char const *arg; + for (i=0; (arg = argv[i]) != NULL; i++) + m_args.push_back(arg); + m_task.StartExceptionThread(); + StartSTDIOThread(); + SetState (eStateAttaching); + int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0); + if (err == 0) + { + m_flags |= eMachProcessFlagsAttached; + DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); + } + else + { + SetState (eStateExited); + DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); + } + } + return m_pid; +} + +#include + +// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, +// or NULL if there was some problem getting the bundle id. +static CFStringRef +CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) +{ + CFBundle bundle(app_bundle_path); + CFStringRef bundleIDCFStr = bundle.GetIdentifier(); + std::string bundleID; + if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + { + struct stat app_bundle_stat; + char err_msg[PATH_MAX]; + + if (::stat (app_bundle_path, &app_bundle_stat) < 0) + { + err_str.SetError(errno, DNBError::POSIX); + snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); + err_str.SetErrorString(err_msg); + DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); + } + else + { + err_str.SetError(-1, DNBError::Generic); + snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); + err_str.SetErrorString(err_msg); + DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); + } + return NULL; + } + + DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); + CFRetain (bundleIDCFStr); + + return bundleIDCFStr; +} + +pid_t +MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], MachProcess* process, DNBError &launch_err) +{ + DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); + CFAllocatorRef alloc = kCFAllocatorDefault; + + if (argv[0] == NULL) + return INVALID_NUB_PROCESS; + + size_t argc = 0; + // Count the number of arguments + while (argv[argc] != NULL) + argc++; + + // Enumerate the arguments + size_t first_launch_arg_idx = 1; + CFReleaser launch_argv; + + if (argv[first_launch_arg_idx]) + { + size_t launch_argc = argc > 0 ? argc - 1 : 0; + launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); + size_t i; + char const *arg; + CFString launch_arg; + for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) + { + launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); + if (launch_arg.get() != NULL) + CFArrayAppendValue(launch_argv.get(), launch_arg.get()); + else + break; + } + } + + // Next fill in the arguments dictionary. Note, the envp array is of the form + // Variable=value but SpringBoard wants a CF dictionary. So we have to convert + // this here. + + CFReleaser launch_envp; + + if (envp[0]) + { + launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + const char *value; + int name_len; + CFString name_string, value_string; + + for (int i = 0; envp[i] != NULL; i++) + { + value = strstr (envp[i], "="); + + // If the name field is empty or there's no =, skip it. Somebody's messing with us. + if (value == NULL || value == envp[i]) + continue; + + name_len = value - envp[i]; + + // Now move value over the "=" + value++; + + name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); + value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); + CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); + } + } + + CFString stdio_path; + + PseudoTerminal pty; + PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); + if (pty_err == PseudoTerminal::success) + { + const char* slave_name = pty.SlaveName(); + DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); + if (slave_name && slave_name[0]) + { + ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); + stdio_path.SetFileSystemRepresentation (slave_name); + } + } + + if (stdio_path.get() == NULL) + { + stdio_path.SetFileSystemRepresentation ("/dev/null"); + } + + CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); + if (bundleIDCFStr == NULL) + return INVALID_NUB_PROCESS; + + std::string bundleID; + CFString::UTF8(bundleIDCFStr, bundleID); + + CFData argv_data(NULL); + + if (launch_argv.get()) + { + if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) + { + DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); + return INVALID_NUB_PROCESS; + } + } + + DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); + + // Find SpringBoard + SBSApplicationLaunchError sbs_error = 0; + sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, + (CFURLRef)NULL, // openURL + launch_argv.get(), + launch_envp.get(), // CFDictionaryRef environment + stdio_path.get(), + stdio_path.get(), + SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); + + + launch_err.SetError(sbs_error, DNBError::SpringBoard); + + if (sbs_error == SBSApplicationLaunchErrorSuccess) + { + static const useconds_t pid_poll_interval = 200000; + static const useconds_t pid_poll_timeout = 30000000; + + useconds_t pid_poll_total = 0; + + nub_process_t pid = INVALID_NUB_PROCESS; + Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired + // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started + // yet, or that it died very quickly (if you weren't using waitForDebugger). + while (!pid_found && pid_poll_total < pid_poll_timeout) + { + usleep (pid_poll_interval); + pid_poll_total += pid_poll_interval; + DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); + pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + } + + CFRelease (bundleIDCFStr); + if (pid_found) + { + if (process != NULL) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); + } + else + { + DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); + } + return pid; + } + + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); + return INVALID_NUB_PROCESS; +} + +#endif // #if defined (__arm__) + + diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h new file mode 100644 index 000000000000..4d5d0d4af921 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -0,0 +1,263 @@ +//===-- MachProcess.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/15/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachProcess_h__ +#define __MachProcess_h__ + +#include "DNBDefs.h" +#include "DNBBreakpoint.h" +#include "DNBError.h" +//#include "MachDYLD.h" +#include "MachException.h" +#include "MachVMMemory.h" +#include "MachTask.h" +#include "MachThreadList.h" +#include "PThreadCondition.h" +#include "PThreadEvent.h" +#include "PThreadMutex.h" + +#include +#include +#include +#include + +class DNBThreadResumeActions; + +class MachProcess +{ +public: + //---------------------------------------------------------------------- + // Constructors and Destructors + //---------------------------------------------------------------------- + MachProcess (); + ~MachProcess (); + + //---------------------------------------------------------------------- + // Child process control + //---------------------------------------------------------------------- + pid_t AttachForDebug (pid_t pid, char *err_str, size_t err_len); + pid_t LaunchForDebug (const char *path, char const *argv[], char const *envp[], const char *stdio_path, nub_launch_flavor_t launch_flavor, DNBError &err); + static pid_t ForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], MachProcess* process, DNBError &err); + static pid_t PosixSpawnChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], const char *stdio_path, MachProcess* process, DNBError& err); + nub_addr_t GetDYLDAllImageInfosAddress (); + static const void * PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str); + static void CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str); + static nub_process_t CheckForProcess (const void *attach_token); +#if defined (__arm__) + pid_t SBLaunchForDebug (const char *app_bundle_path, char const *argv[], char const *envp[], DNBError &launch_err); + static pid_t SBForkChildForPTraceDebugging (const char *path, char const *argv[], char const *envp[], MachProcess* process, DNBError &launch_err); +#endif + nub_addr_t LookupSymbol (const char *name, const char *shlib); + void SetNameToAddressCallback (DNBCallbackNameToAddress callback, void *baton) + { + m_name_to_addr_callback = callback; + m_name_to_addr_baton = baton; + } + void SetSharedLibraryInfoCallback (DNBCallbackCopyExecutableImageInfos callback, void *baton) + { + m_image_infos_callback = callback; + m_image_infos_baton = baton; + } + + bool Resume (const DNBThreadResumeActions& thread_actions); + bool Signal (int signal, const struct timespec *timeout_abstime = NULL); + bool Kill (const struct timespec *timeout_abstime = NULL); + bool Detach (); + nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf); + nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf); + + //---------------------------------------------------------------------- + // Path and arg accessors + //---------------------------------------------------------------------- + const char * Path () const { return m_path.c_str(); } + size_t ArgumentCount () const { return m_args.size(); } + const char * ArgumentAtIndex (size_t arg_idx) const + { + if (arg_idx < m_args.size()) + return m_args[arg_idx].c_str(); + return NULL; + } + + //---------------------------------------------------------------------- + // Breakpoint functions + //---------------------------------------------------------------------- + nub_break_t CreateBreakpoint (nub_addr_t addr, nub_size_t length, bool hardware, thread_t thread); + bool DisableBreakpoint (nub_break_t breakID, bool remove); + nub_size_t DisableAllBreakpoints (bool remove); + bool EnableBreakpoint (nub_break_t breakID); + void DumpBreakpoint(nub_break_t breakID) const; + DNBBreakpointList& Breakpoints() { return m_breakpoints; } + const DNBBreakpointList& Breakpoints() const { return m_breakpoints; } + + //---------------------------------------------------------------------- + // Watchpoint functions + //---------------------------------------------------------------------- + nub_watch_t CreateWatchpoint (nub_addr_t addr, nub_size_t length, uint32_t watch_type, bool hardware, thread_t thread); + bool DisableWatchpoint (nub_watch_t watchID, bool remove); + nub_size_t DisableAllWatchpoints (bool remove); + bool EnableWatchpoint (nub_watch_t watchID); + void DumpWatchpoint(nub_watch_t watchID) const; + DNBBreakpointList& Watchpoints() { return m_watchpoints; } + const DNBBreakpointList& Watchpoints() const { return m_watchpoints; } + + //---------------------------------------------------------------------- + // Exception thread functions + //---------------------------------------------------------------------- + bool StartSTDIOThread (); + static void * STDIOThread (void *arg); + void ExceptionMessageReceived (const MachException::Message& exceptionMessage); + void ExceptionMessageBundleComplete (); + void SharedLibrariesUpdated (); + nub_size_t CopyImageInfos (struct DNBExecutableImageInfo **image_infos, bool only_changed); + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + pid_t ProcessID () const { return m_pid; } + bool ProcessIDIsValid () const { return m_pid > 0; } + pid_t SetProcessID (pid_t pid); + MachTask& Task() { return m_task; } + const MachTask& Task() const { return m_task; } + + PThreadEvent& Events() { return m_events; } + const DNBRegisterSetInfo * + GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const; + bool GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value) const; + bool SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value) const; + const char * ThreadGetName (nub_thread_t tid); + nub_state_t ThreadGetState (nub_thread_t tid); + nub_size_t GetNumThreads () const; + nub_thread_t GetThreadAtIndex (nub_size_t thread_idx) const; + uint32_t GetThreadIndexFromThreadID (nub_thread_t tid); + nub_thread_t GetCurrentThread (); + nub_thread_t SetCurrentThread (nub_thread_t tid); + MachThreadList & GetThreadList() { return m_threadList; } + bool GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const; + void DumpThreadStoppedReason(nub_thread_t tid) const; + const char * GetThreadInfo (nub_thread_t tid) const; + + nub_state_t GetState (); + void SetState (nub_state_t state); + bool IsRunning (nub_state_t state) + { + return state == eStateRunning || IsStepping(state); + } + bool IsStepping (nub_state_t state) + { + return state == eStateStepping; + } + bool CanResume (nub_state_t state) + { + return state == eStateStopped; + } + + const DNBError& GetLastError () const { return m_err; } + + bool GetExitStatus(int* status) + { + if (GetState() == eStateExited) + { + if (status) + *status = m_exit_status; + return true; + } + return false; + } + void SetExitStatus(int status) + { + m_exit_status = status; + SetState(eStateExited); + } + + uint32_t StopCount() const { return m_stop_count; } + void SetChildFileDescriptors (int stdin_fileno, int stdout_fileno, int stderr_fileno) + { + m_child_stdin = stdin_fileno; + m_child_stdout = stdout_fileno; + m_child_stderr = stderr_fileno; + } + + int GetStdinFileDescriptor () const { return m_child_stdin; } + int GetStdoutFileDescriptor () const { return m_child_stdout; } + int GetStderrFileDescriptor () const { return m_child_stderr; } + void AppendSTDOUT (char* s, size_t len); + size_t GetAvailableSTDOUT (char *buf, size_t buf_size); + size_t GetAvailableSTDERR (char *buf, size_t buf_size); + void CloseChildFileDescriptors () + { + if (m_child_stdin >= 0) + { + ::close (m_child_stdin); + m_child_stdin = -1; + } + if (m_child_stdout >= 0) + { + ::close (m_child_stdout); + m_child_stdout = -1; + } + if (m_child_stderr >= 0) + { + ::close (m_child_stderr); + m_child_stderr = -1; + } + } + + bool ProcessUsingSpringBoard() const { return (m_flags & eMachProcessFlagsUsingSBS) != 0; } +private: + enum + { + eMachProcessFlagsNone = 0, + eMachProcessFlagsAttached = (1 << 0), + eMachProcessFlagsUsingSBS = (1 << 1) + }; + void Clear (); + void ReplyToAllExceptions (const DNBThreadResumeActions& thread_actions); + void PrivateResume (const DNBThreadResumeActions& thread_actions); + nub_size_t RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const; + + uint32_t Flags () const { return m_flags; } + nub_state_t DoSIGSTOP (bool clear_bps_and_wps); + + pid_t m_pid; // Process ID of child process + int m_child_stdin; + int m_child_stdout; + int m_child_stderr; + std::string m_path; // A path to the executable if we have one + std::vector m_args; // The arguments with which the process was lauched + int m_exit_status; // The exit status for the process + MachTask m_task; // The mach task for this process + uint32_t m_flags; // Process specific flags (see eMachProcessFlags enums) + uint32_t m_stop_count; // A count of many times have we stopped + pthread_t m_stdio_thread; // Thread ID for the thread that watches for child process stdio + PThreadMutex m_stdio_mutex; // Multithreaded protection for stdio + std::string m_stdout_data; + MachException::Message::collection + m_exception_messages; // A collection of exception messages caught when listening to the exception port + PThreadMutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages + + MachThreadList m_threadList; // A list of threads that is maintained/updated after each stop + DNBError m_err; // The last error for any transaction + nub_state_t m_state; // The state of our process + PThreadMutex m_state_mutex; // Multithreaded protection for m_state + PThreadEvent m_events; // Process related events in the child processes lifetime can be waited upon + DNBBreakpointList m_breakpoints; // Breakpoint list for this process + DNBBreakpointList m_watchpoints; // Watchpoint list for this process + DNBCallbackNameToAddress m_name_to_addr_callback; + void * m_name_to_addr_baton; + DNBCallbackCopyExecutableImageInfos + m_image_infos_callback; + void * m_image_infos_baton; +}; + + +#endif // __MachProcess_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.cpp b/lldb/tools/debugserver/source/MacOSX/MachTask.cpp new file mode 100644 index 000000000000..68d858c014ce --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.cpp @@ -0,0 +1,660 @@ +//===-- MachTask.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// MachTask.cpp +// debugserver +// +// Created by Greg Clayton on 12/5/08. +// +//===----------------------------------------------------------------------===// + +#include "MachTask.h" + +// C Includes + +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "CFUtils.h" +#include "DNB.h" +#include "DNBError.h" +#include "DNBLog.h" +#include "MachProcess.h" +#include "DNBDataRef.h" + +#if defined (__arm__) + +#include +#include +#include + +#endif + +//---------------------------------------------------------------------- +// MachTask constructor +//---------------------------------------------------------------------- +MachTask::MachTask(MachProcess *process) : + m_process (process), + m_task (TASK_NULL), + m_vm_memory (), + m_exception_thread (0), + m_exception_port (MACH_PORT_NULL) +{ + memset(&m_exc_port_info, 0, sizeof(m_exc_port_info)); + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +MachTask::~MachTask() +{ + Clear(); +} + + +//---------------------------------------------------------------------- +// MachTask::Suspend +//---------------------------------------------------------------------- +kern_return_t +MachTask::Suspend() +{ + DNBError err; + task_t task = TaskPort(); + err = ::task_suspend (task); + if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) + err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task); + return err.Error(); +} + + +//---------------------------------------------------------------------- +// MachTask::Resume +//---------------------------------------------------------------------- +kern_return_t +MachTask::Resume() +{ + struct task_basic_info task_info; + task_t task = TaskPort(); + + DNBError err; + err = BasicInfo(task, &task_info); + + if (err.Success()) + { + integer_t i; + for (i=0; i %u bytes read", (uint64_t)addr, size, buf, n); + if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DNBDataRef data((uint8_t*)buf, n, false); + data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16); + } + } + return n; +} + + +//---------------------------------------------------------------------- +// MachTask::WriteMemory +//---------------------------------------------------------------------- +nub_size_t +MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) +{ + nub_size_t n = 0; + task_t task = TaskPort(); + if (task != TASK_NULL) + { + n = m_vm_memory.Write(task, addr, buf, size); + DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n); + if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DNBDataRef data((uint8_t*)buf, n, false); + data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16); + } + } + return n; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::TaskPortForProcessID (DNBError &err) +{ + if (m_task == TASK_NULL && m_process != NULL) + m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err); + return m_task; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::TaskPortForProcessID (pid_t pid, DNBError &err) +{ + task_t task = TASK_NULL; + if (pid != INVALID_NUB_PROCESS) + { + mach_port_t task_self = mach_task_self (); + err = ::task_for_pid ( task_self, pid, &task); + if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) + { + char str[1024]; + ::snprintf (str, + sizeof(str), + "::task_for_pid ( task_self, pid = %d, task => TASK_NULL (0x%4.4x) ) uid=%u, euid=%u gid=%u egid=%u (%s)", + pid, + task, + getuid(), + geteuid(), + getgid(), + getegid(), + err.AsString() ? err.AsString() : "success"); + if (err.Fail()) + err.SetErrorString(str); + err.LogThreaded(str); + } + } + return task; +} + + +//---------------------------------------------------------------------- +// MachTask::BasicInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::BasicInfo(struct task_basic_info *info) +{ + return BasicInfo (TaskPort(), info); +} + +//---------------------------------------------------------------------- +// MachTask::BasicInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::BasicInfo(task_t task, struct task_basic_info *info) +{ + if (info == NULL) + return KERN_INVALID_ARGUMENT; + + DNBError err; + mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; + err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count); + const bool log_process = DNBLogCheckLogBit(LOG_TASK); + if (log_process || err.Fail()) + err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count); + if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && err.Success()) + { + float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; + float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; + DNBLogThreaded("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8x, resident_size = 0x%8.8x, user_time = %f, system_time = %f }", + info->suspend_count, info->virtual_size, info->resident_size, user, system); + } + return err.Error(); +} + + +//---------------------------------------------------------------------- +// MachTask::IsValid +// +// Returns true if a task is a valid task port for a current process. +//---------------------------------------------------------------------- +bool +MachTask::IsValid () const +{ + return MachTask::IsValid(TaskPort()); +} + +//---------------------------------------------------------------------- +// MachTask::IsValid +// +// Returns true if a task is a valid task port for a current process. +//---------------------------------------------------------------------- +bool +MachTask::IsValid (task_t task) +{ + if (task != TASK_NULL) + { + struct task_basic_info task_info; + return BasicInfo(task, &task_info) == KERN_SUCCESS; + } + return false; +} + + +bool +MachTask::StartExceptionThread(DNBError &err) +{ + DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__); + task_t task = TaskPortForProcessID(err); + if (MachTask::IsValid(task)) + { + // Got the mach port for the current process + mach_port_t task_self = mach_task_self (); + + // Allocate an exception port that we will use to track our child process + err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port); + if (err.Fail()) + return false; + + // Add the ability to send messages on the new exception port + err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND); + if (err.Fail()) + return false; + + // Save the original state of the exception ports for our child process + SaveExceptionPortInfo(); + + // Set the ability to get all exceptions on this port + err = ::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); + if (err.Fail()) + return false; + + // Create the exception thread + err = ::pthread_create (&m_exception_thread, NULL, MachTask::ExceptionThread, this); + return err.Success(); + } + else + { + DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", __FUNCTION__); + } + return false; +} + +kern_return_t +MachTask::ShutDownExcecptionThread() +{ + DNBError err; + + err = RestoreExceptionPortInfo(); + + // NULL our our exception port and let our exception thread exit + mach_port_t exception_port = m_exception_port; + m_exception_port = NULL; + + err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX); + if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) + err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread); + + err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX); + if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) + err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", m_exception_thread); + + // Deallocate our exception port that we used to track our child process + mach_port_t task_self = mach_task_self (); + err = ::mach_port_deallocate (task_self, exception_port); + if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) + err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port); + exception_port = NULL; + + return err.Error(); +} + + +void * +MachTask::ExceptionThread (void *arg) +{ + if (arg == NULL) + return NULL; + + MachTask *mach_task = (MachTask*) arg; + MachProcess *mach_proc = mach_task->Process(); + DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, arg); + + // We keep a count of the number of consecutive exceptions received so + // we know to grab all exceptions without a timeout. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main loop in this + // thread can stop periodically if needed to service things related to this + // process. + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + uint32_t num_exceptions_received = 0; + DNBError err; + task_t task = mach_task->TaskPort(); + mach_msg_timeout_t periodic_timeout = 0; + +#if defined (__arm__) + mach_msg_timeout_t watchdog_elapsed = 0; + mach_msg_timeout_t watchdog_timeout = 60 * 1000; + pid_t pid = mach_proc->ProcessID(); + CFReleaser watchdog; + + if (mach_proc->ProcessUsingSpringBoard()) + { + // Request a renewal for every 60 seconds if we attached using SpringBoard + watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60)); + DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get()); + + if (watchdog.get()) + { + ::SBSWatchdogAssertionRenew (watchdog.get()); + + CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get()); + DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval); + if (watchdogRenewalInterval > 0.0) + { + watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000; + if (watchdog_timeout > 3000) + watchdog_timeout -= 1000; // Give us a second to renew our timeout + else if (watchdog_timeout > 1000) + watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout + } + } + if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout) + periodic_timeout = watchdog_timeout; + } +#endif // #if defined (__arm__) + + while (mach_task->ExceptionPortIsValid()) + { + ::pthread_testcancel (); + + MachException::Message exception_message; + + + if (num_exceptions_received > 0) + { + // No timeout, just receive as many exceptions as we can since we already have one and we want + // to get all currently available exceptions for this task + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0); + } + else if (periodic_timeout > 0) + { + // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms) + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout); + } + else + { + // We don't need to parse all current exceptions or stop periodically, + // just wait for an exception forever. + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0); + } + + if (err.Error() == MACH_RCV_INTERRUPTED) + { + // If we have no task port we should exit this thread + if (!mach_task->ExceptionPortIsValid()) + { + DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled..."); + break; + } + + // Make sure our task is still valid + if (MachTask::IsValid(task)) + { + // Task is still ok + DNBLogThreadedIf(LOG_EXCEPTIONS, "interrupted, but task still valid, continuing..."); + continue; + } + else + { + DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited..."); + mach_proc->SetState(eStateExited); + // Our task has died, exit the thread. + break; + } + } + else if (err.Error() == MACH_RCV_TIMED_OUT) + { + if (num_exceptions_received > 0) + { + // We were receiving all current exceptions with a timeout of zero + // it is time to go back to our normal looping mode + num_exceptions_received = 0; + + // Notify our main thread we have a complete exception message + // bundle available. + mach_proc->ExceptionMessageBundleComplete(); + + // in case we use a timeout value when getting exceptions... + // Make sure our task is still valid + if (MachTask::IsValid(task)) + { + // Task is still ok + DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing..."); + continue; + } + else + { + DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited..."); + mach_proc->SetState(eStateExited); + // Our task has died, exit the thread. + break; + } + continue; + } + +#if defined (__arm__) + if (watchdog.get()) + { + watchdog_elapsed += periodic_timeout; + if (watchdog_elapsed >= watchdog_timeout) + { + DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get()); + ::SBSWatchdogAssertionRenew (watchdog.get()); + watchdog_elapsed = 0; + } + } +#endif + } + else if (err.Error() != KERN_SUCCESS) + { + DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something about it??? nah, continuing for now..."); + // TODO: notify of error? + } + else + { + if (exception_message.CatchExceptionRaise()) + { + ++num_exceptions_received; + mach_proc->ExceptionMessageReceived(exception_message); + } + } + } + +#if defined (__arm__) + if (watchdog.get()) + { + // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we + // all are up and running on systems that support it. The SBS framework has a #define + // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now + // so it should still build either way. + DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get()); + ::SBSWatchdogAssertionRelease (watchdog.get()); + } +#endif // #if defined (__arm__) + + DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", __FUNCTION__, arg); + return NULL; +} + + +// So the TASK_DYLD_INFO used to just return the address of the all image infos +// as a single member called "all_image_info". Then someone decided it would be +// a good idea to rename this first member to "all_image_info_addr" and add a +// size member called "all_image_info_size". This of course can not be detected +// using code or #defines. So to hack around this problem, we define our own +// version of the TASK_DYLD_INFO structure so we can guarantee what is inside it. + +struct hack_task_dyld_info { + mach_vm_address_t all_image_info_addr; + mach_vm_size_t all_image_info_size; +}; + +nub_addr_t +MachTask::GetDYLDAllImageInfosAddress (DNBError& err) +{ + struct hack_task_dyld_info dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + // Make sure that COUNT isn't bigger than our hacked up struct hack_task_dyld_info. + // If it is, then make COUNT smaller to match. + if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t))) + count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)); + + task_t task = TaskPortForProcessID (err); + if (err.Success()) + { + err = ::task_info (task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); + if (err.Success()) + { + // We now have the address of the all image infos structure + return dyld_info.all_image_info_addr; + } + } + return INVALID_NUB_ADDRESS; +} + + +//---------------------------------------------------------------------- +// MachTask::AllocateMemory +//---------------------------------------------------------------------- +nub_addr_t +MachTask::AllocateMemory (size_t size, uint32_t permissions) +{ + mach_vm_address_t addr; + task_t task = TaskPort(); + if (task == TASK_NULL) + return INVALID_NUB_ADDRESS; + + DNBError err; + err = ::mach_vm_allocate (task, &addr, size, TRUE); + if (err.Error() == KERN_SUCCESS) + { + // Set the protections: + vm_prot_t mach_prot = 0; + if (permissions & eMemoryPermissionsReadable) + mach_prot |= VM_PROT_READ; + if (permissions & eMemoryPermissionsWritable) + mach_prot |= VM_PROT_WRITE; + if (permissions & eMemoryPermissionsExecutable) + mach_prot |= VM_PROT_EXECUTE; + + + err = ::mach_vm_protect (task, addr, size, 0, mach_prot); + if (err.Error() == KERN_SUCCESS) + { + m_allocations.insert (std::make_pair(addr, size)); + return addr; + } + ::mach_vm_deallocate (task, addr, size); + } + return INVALID_NUB_ADDRESS; +} + +//---------------------------------------------------------------------- +// MachTask::DeallocateMemory +//---------------------------------------------------------------------- +nub_bool_t +MachTask::DeallocateMemory (nub_addr_t addr) +{ + task_t task = TaskPort(); + if (task == TASK_NULL) + return false; + + // We have to stash away sizes for the allocations... + allocation_collection::iterator pos, end = m_allocations.end(); + for (pos = m_allocations.begin(); pos != end; pos++) + { + if ((*pos).first == addr) + { + m_allocations.erase(pos); + return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS; + } + + } + return false; +} + diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.h b/lldb/tools/debugserver/source/MacOSX/MachTask.h new file mode 100644 index 000000000000..3bf40e134571 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.h @@ -0,0 +1,91 @@ +//===-- MachTask.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// MachTask.h +// debugserver +// +// Created by Greg Clayton on 12/5/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachTask_h__ +#define __MachTask_h__ + +// C Includes +#include +#include +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "MachException.h" +#include "MachVMMemory.h" +#include "PThreadMutex.h" + +class MachProcess; + +class MachTask +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + MachTask (MachProcess *process); + virtual ~MachTask (); + + void Clear (); + + kern_return_t Suspend (); + kern_return_t Resume (); + + nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf); + nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf); + + nub_addr_t AllocateMemory (nub_size_t size, uint32_t permissions); + nub_bool_t DeallocateMemory (nub_addr_t addr); + + mach_port_t ExceptionPort () const; + bool ExceptionPortIsValid () const; + kern_return_t SaveExceptionPortInfo (); + kern_return_t RestoreExceptionPortInfo (); + kern_return_t ShutDownExcecptionThread (); + + bool StartExceptionThread (DNBError &err); + nub_addr_t GetDYLDAllImageInfosAddress (DNBError& err); + kern_return_t BasicInfo (struct task_basic_info *info); + static kern_return_t BasicInfo (task_t task, struct task_basic_info *info); + bool IsValid () const; + static bool IsValid (task_t task); + static void * ExceptionThread (void *arg); + task_t TaskPort () const { return m_task; } + task_t TaskPortForProcessID (DNBError &err); + static task_t TaskPortForProcessID (pid_t pid, DNBError &err); + + MachProcess * Process () { return m_process; } + const MachProcess * Process () const { return m_process; } + +protected: + MachProcess * m_process; // The mach process that owns this MachTask + task_t m_task; + MachVMMemory m_vm_memory; // Special mach memory reading class that will take care of watching for page and region boundaries + MachException::PortInfo + m_exc_port_info; // Saved settings for all exception ports + pthread_t m_exception_thread; // Thread ID for the exception thread in case we need it + mach_port_t m_exception_port; // Exception port on which we will receive child exceptions + + typedef std::map allocation_collection; + allocation_collection m_allocations; + +private: + MachTask(const MachTask&); // Outlaw + MachTask& operator=(const MachTask& rhs);// Outlaw +}; + +#endif // __MachTask_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/MachThread.cpp b/lldb/tools/debugserver/source/MacOSX/MachThread.cpp new file mode 100644 index 000000000000..9d018f66ea6c --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachThread.cpp @@ -0,0 +1,745 @@ +//===-- MachThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/19/07. +// +//===----------------------------------------------------------------------===// + +#include "MachThread.h" +#include "MachProcess.h" +#include "DNBLog.h" +#include "DNB.h" + +static uint32_t +GetSequenceID() +{ + static uint32_t g_nextID = 0; + return ++g_nextID; +} + +MachThread::MachThread (MachProcess *process, thread_t thread) : + m_process(process), + m_tid(thread), + m_seq_id(GetSequenceID()), + m_state(eStateUnloaded), + m_state_mutex(PTHREAD_MUTEX_RECURSIVE), + m_breakID(INVALID_NUB_BREAK_ID), + m_suspendCount(0), + m_arch(this), + m_regSets() +{ + nub_size_t num_reg_sets = 0; + const DNBRegisterSetInfo *regSetInfo = m_arch.GetRegisterSetInfo(&num_reg_sets); + if (num_reg_sets > 0) + m_regSets.assign(regSetInfo, regSetInfo + num_reg_sets); + + ::memset (&m_basicInfo, 0, sizeof (m_basicInfo)); + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::MachThread ( process = %p, tid = 0x%4.4x, seq_id = %u )", &m_process, m_tid, m_seq_id); +} + +MachThread::~MachThread() +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::~MachThread() for tid = 0x%4.4x (%u)", m_tid, m_seq_id); +} + + + +uint32_t +MachThread::Suspend() +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + if (ThreadIDIsValid(m_tid)) + { + DNBError err(::thread_suspend (m_tid), DNBError::MachKernel); + if (err.Success()) + m_suspendCount++; + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_suspend (%4.4x)", m_tid); + } + return SuspendCount(); +} + +uint32_t +MachThread::Resume() +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + if (ThreadIDIsValid(m_tid)) + { + while (m_suspendCount > 0) + { + DNBError err(::thread_resume (m_tid), DNBError::MachKernel); + if (err.Success()) + m_suspendCount--; + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_resume (%4.4x)", m_tid); + } + } + return SuspendCount(); +} + +bool +MachThread::RestoreSuspendCount() +{ + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )", __FUNCTION__); + DNBError err; + if (ThreadIDIsValid(m_tid) == false) + return false; + else if (m_suspendCount > m_basicInfo.suspend_count) + { + while (m_suspendCount > m_basicInfo.suspend_count) + { + err = ::thread_resume (m_tid); + if (err.Success()) + --m_suspendCount; + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_resume (%4.4x)", m_tid); + } + } + else if (m_suspendCount < m_basicInfo.suspend_count) + { + while (m_suspendCount < m_basicInfo.suspend_count) + { + err = ::thread_suspend (m_tid); + if (err.Success()) + --m_suspendCount; + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::thread_suspend (%4.4x)", m_tid); + } + } + return m_suspendCount == m_basicInfo.suspend_count; +} + + +const char * +MachThread::GetBasicInfoAsString () const +{ + static char g_basic_info_string[1024]; + struct thread_basic_info basicInfo; + + if (GetBasicInfo(m_tid, &basicInfo)) + { + +// char run_state_str[32]; +// size_t run_state_str_size = sizeof(run_state_str); +// switch (basicInfo.run_state) +// { +// case TH_STATE_RUNNING: strncpy(run_state_str, "running", run_state_str_size); break; +// case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", run_state_str_size); break; +// case TH_STATE_WAITING: strncpy(run_state_str, "waiting", run_state_str_size); break; +// case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, "uninterruptible", run_state_str_size); break; +// case TH_STATE_HALTED: strncpy(run_state_str, "halted", run_state_str_size); break; +// default: snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break; // ??? +// } + float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f; + float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f; + snprintf(g_basic_info_string, sizeof(g_basic_info_string), "Thread 0x%4.4x: user=%f system=%f cpu=%d sleep_time=%d", + InferiorThreadID(), + user, + system, + basicInfo.cpu_usage, + basicInfo.sleep_time); + + return g_basic_info_string; + } + return NULL; +} + +thread_t +MachThread::InferiorThreadID() const +{ + mach_msg_type_number_t i; + mach_port_name_array_t names; + mach_port_type_array_t types; + mach_msg_type_number_t ncount, tcount; + thread_t inferior_tid = INVALID_NUB_THREAD; + task_t my_task = ::mach_task_self(); + task_t task = m_process->Task().TaskPort(); + + kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount); + if (kret == KERN_SUCCESS) + { + + for (i = 0; i < ncount; i++) + { + mach_port_t my_name; + mach_msg_type_name_t my_type; + + kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type); + if (kret == KERN_SUCCESS) + { + ::mach_port_deallocate (my_task, my_name); + if (my_name == m_tid) + { + inferior_tid = names[i]; + break; + } + } + } + // Free up the names and types + ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t)); + ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t)); + } + return inferior_tid; +} + +bool +MachThread::GetBasicInfo(thread_t thread, struct thread_basic_info *basicInfoPtr) +{ + if (ThreadIDIsValid(thread)) + { + unsigned int info_count = THREAD_BASIC_INFO_COUNT; + kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count); + if (err == KERN_SUCCESS) + return true; + } + ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info)); + return false; +} + + +bool +MachThread::ThreadIDIsValid(thread_t thread) +{ + return thread != THREAD_NULL; +} + +bool +MachThread::GetRegisterState(int flavor, bool force) +{ + return m_arch.GetRegisterState(flavor, force) == KERN_SUCCESS; +} + +bool +MachThread::SetRegisterState(int flavor) +{ + return m_arch.SetRegisterState(flavor) == KERN_SUCCESS; +} + +uint64_t +MachThread::GetPC(uint64_t failValue) +{ + // Get program counter + return m_arch.GetPC(failValue); +} + +bool +MachThread::SetPC(uint64_t value) +{ + // Set program counter + return m_arch.SetPC(value); +} + +uint64_t +MachThread::GetSP(uint64_t failValue) +{ + // Get stack pointer + return m_arch.GetSP(failValue); +} + +nub_process_t +MachThread::ProcessID() const +{ + if (m_process) + return m_process->ProcessID(); + return INVALID_NUB_PROCESS; +} + +void +MachThread::Dump(uint32_t index) +{ + const char * thread_run_state = NULL; + + switch (m_basicInfo.run_state) + { + case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally + case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped + case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally + case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait + case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a + default: thread_run_state = "???"; break; + } + + DNBLogThreaded("thread[%u] %4.4x (%u): pc: 0x%8.8llx sp: 0x%8.8llx breakID: %d user: %d.%06.6d system: %d.%06.6d cpu: %d policy: %d run_state: %d (%s) flags: %d suspend_count: %d (current %d) sleep_time: %d", + index, + m_tid, + m_seq_id, + GetPC(INVALID_NUB_ADDRESS), + GetSP(INVALID_NUB_ADDRESS), + m_breakID, + m_basicInfo.user_time.seconds, m_basicInfo.user_time.microseconds, + m_basicInfo.system_time.seconds, m_basicInfo.system_time.microseconds, + m_basicInfo.cpu_usage, + m_basicInfo.policy, + m_basicInfo.run_state, + thread_run_state, + m_basicInfo.flags, + m_basicInfo.suspend_count, m_suspendCount, + m_basicInfo.sleep_time); + //DumpRegisterState(0); +} + +void +MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action) +{ + if (thread_action->addr != INVALID_NUB_ADDRESS) + SetPC (thread_action->addr); + + SetState (thread_action->state); + switch (thread_action->state) + { + case eStateStopped: + case eStateSuspended: + Suspend(); + break; + + case eStateRunning: + case eStateStepping: + Resume(); + break; + } + m_arch.ThreadWillResume(); + m_stop_exception.Clear(); +} + +bool +MachThread::ShouldStop(bool &step_more) +{ + // See if this thread is at a breakpoint? + nub_break_t breakID = CurrentBreakpoint(); + + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + // This thread is sitting at a breakpoint, ask the breakpoint + // if we should be stopping here. + if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID)) + return true; + else + { + // The breakpoint said we shouldn't stop, but we may have gotten + // a signal or the user may have requested to stop in some other + // way. Stop if we have a valid exception (this thread won't if + // another thread was the reason this process stopped) and that + // exception, is NOT a breakpoint exception (a common case would + // be a SIGINT signal). + if (GetStopException().IsValid() && !GetStopException().IsBreakpoint()) + return true; + } + } + else + { + if (m_arch.StepNotComplete()) + { + step_more = true; + return false; + } + // The thread state is used to let us know what the thread was + // trying to do. MachThread::ThreadWillResume() will set the + // thread state to various values depending if the thread was + // the current thread and if it was to be single stepped, or + // resumed. + if (GetState() == eStateRunning) + { + // If our state is running, then we should continue as we are in + // the process of stepping over a breakpoint. + return false; + } + else + { + // Stop if we have any kind of valid exception for this + // thread. + if (GetStopException().IsValid()) + return true; + } + } + return false; +} +bool +MachThread::IsStepping() +{ + // Return true if this thread is currently being stepped. + // MachThread::ThreadWillResume currently determines this by looking if we + // have been asked to single step, or if we are at a breakpoint instruction + // and have been asked to resume. In the latter case we need to disable the + // breakpoint we are at, single step, re-enable and continue. + nub_state_t state = GetState(); + return (state == eStateStepping) || + (state == eStateRunning && NUB_BREAK_ID_IS_VALID(CurrentBreakpoint())); +} + + +bool +MachThread::ThreadDidStop() +{ + // This thread has existed prior to resuming under debug nub control, + // and has just been stopped. Do any cleanup that needs to be done + // after running. + + // The thread state and breakpoint will still have the same values + // as they had prior to resuming the thread, so it makes it easy to check + // if we were trying to step a thread, or we tried to resume while being + // at a breakpoint. + + // When this method gets called, the process state is still in the + // state it was in while running so we can act accordingly. + m_arch.ThreadDidStop(); + + + // We may have suspended this thread so the primary thread could step + // without worrying about race conditions, so lets restore our suspend + // count. + RestoreSuspendCount(); + + // Update the basic information for a thread + MachThread::GetBasicInfo(m_tid, &m_basicInfo); + m_suspendCount = m_basicInfo.suspend_count; + + // See if we were at a breakpoint when we last resumed that we disabled, + // re-enable it. + nub_break_t breakID = CurrentBreakpoint(); + + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + m_process->EnableBreakpoint(breakID); + if (m_suspendCount > 0) + { + SetState(eStateSuspended); + } + else + { + // If we last were at a breakpoint and we single stepped, our state + // will be "running" to indicate we need to continue after stepping + // over the breakpoint instruction. If we step over a breakpoint + // instruction, we need to stop. + if (GetState() == eStateRunning) + { + // Leave state set to running so we will continue automatically + // from this breakpoint + } + else + { + SetState(eStateStopped); + } + } + } + else + { + if (m_suspendCount > 0) + { + SetState(eStateSuspended); + } + else + { + SetState(eStateStopped); + } + } + + + SetCurrentBreakpoint(INVALID_NUB_BREAK_ID); + + return true; +} + +bool +MachThread::NotifyException(MachException::Data& exc) +{ + if (m_stop_exception.IsValid()) + { + // We may have more than one exception for a thread, but we need to + // only remember the one that we will say is the reason we stopped. + // We may have been single stepping and also gotten a signal exception, + // so just remember the most pertinent one. + if (m_stop_exception.IsBreakpoint()) + m_stop_exception = exc; + } + else + { + m_stop_exception = exc; + } + bool handled = m_arch.NotifyException(exc); + if (!handled) + { + handled = true; + nub_addr_t pc = GetPC(); + nub_break_t breakID = m_process->Breakpoints().FindIDByAddress(pc); + SetCurrentBreakpoint(breakID); + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + } + return handled; +} + + +nub_state_t +MachThread::GetState() +{ + // If any other threads access this we will need a mutex for it + PTHREAD_MUTEX_LOCKER (locker, m_state_mutex); + return m_state; +} + +void +MachThread::SetState(nub_state_t state) +{ + PTHREAD_MUTEX_LOCKER (locker, m_state_mutex); + m_state = state; + DNBLogThreadedIf(LOG_THREAD, "MachThread::SetState ( %s ) for tid = 0x%4.4x", DNBStateAsString(state), m_tid); +} + +uint32_t +MachThread::GetNumRegistersInSet(int regSet) const +{ + if (regSet < m_regSets.size()) + return m_regSets[regSet].num_registers; + return 0; +} + +const char * +MachThread::GetRegisterSetName(int regSet) const +{ + if (regSet < m_regSets.size()) + return m_regSets[regSet].name; + return NULL; +} + +const DNBRegisterInfo * +MachThread::GetRegisterInfo(int regSet, int regIndex) const +{ + if (regSet < m_regSets.size()) + if (regIndex < m_regSets[regSet].num_registers) + return &m_regSets[regSet].registers[regIndex]; + return NULL; +} +void +MachThread::DumpRegisterState(int regSet) +{ + if (regSet == REGISTER_SET_ALL) + { + for (regSet = 1; regSet < m_regSets.size(); regSet++) + DumpRegisterState(regSet); + } + else + { + if (m_arch.RegisterSetStateIsValid(regSet)) + { + const size_t numRegisters = GetNumRegistersInSet(regSet); + size_t regIndex = 0; + DNBRegisterValueClass reg; + for (regIndex = 0; regIndex < numRegisters; ++regIndex) + { + if (m_arch.GetRegisterValue(regSet, regIndex, ®)) + { + reg.Dump(NULL, NULL); + } + } + } + else + { + DNBLog("%s: registers are not currently valid.", GetRegisterSetName(regSet)); + } + } +} + +const DNBRegisterSetInfo * +MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets ) const +{ + *num_reg_sets = m_regSets.size(); + return &m_regSets[0]; +} + +bool +MachThread::GetRegisterValue ( uint32_t set, uint32_t reg, DNBRegisterValue *value ) +{ + return m_arch.GetRegisterValue(set, reg, value); +} + +bool +MachThread::SetRegisterValue ( uint32_t set, uint32_t reg, const DNBRegisterValue *value ) +{ + return m_arch.SetRegisterValue(set, reg, value); +} + +nub_size_t +MachThread::GetRegisterContext (void *buf, nub_size_t buf_len) +{ + return m_arch.GetRegisterContext(buf, buf_len); +} + +nub_size_t +MachThread::SetRegisterContext (const void *buf, nub_size_t buf_len) +{ + return m_arch.SetRegisterContext(buf, buf_len); +} + +uint32_t +MachThread::EnableHardwareBreakpoint (const DNBBreakpoint *bp) +{ + if (bp != NULL && bp->IsBreakpoint()) + return m_arch.EnableHardwareBreakpoint(bp->Address(), bp->ByteSize()); + return INVALID_NUB_HW_INDEX; +} + +uint32_t +MachThread::EnableHardwareWatchpoint (const DNBBreakpoint *wp) +{ + if (wp != NULL && wp->IsWatchpoint()) + return m_arch.EnableHardwareWatchpoint(wp->Address(), wp->ByteSize(), wp->WatchpointRead(), wp->WatchpointWrite()); + return INVALID_NUB_HW_INDEX; +} + +bool +MachThread::DisableHardwareBreakpoint (const DNBBreakpoint *bp) +{ + if (bp != NULL && bp->IsHardware()) + return m_arch.DisableHardwareBreakpoint(bp->GetHardwareIndex()); + return false; +} + +bool +MachThread::DisableHardwareWatchpoint (const DNBBreakpoint *wp) +{ + if (wp != NULL && wp->IsHardware()) + return m_arch.DisableHardwareWatchpoint(wp->GetHardwareIndex()); + return false; +} + + +void +MachThread::NotifyBreakpointChanged (const DNBBreakpoint *bp) +{ + nub_break_t breakID = bp->GetID(); + if (bp->IsEnabled()) + { + if (bp->Address() == GetPC()) + { + SetCurrentBreakpoint(breakID); + } + } + else + { + if (CurrentBreakpoint() == breakID) + { + SetCurrentBreakpoint(INVALID_NUB_BREAK_ID); + } + } +} + +bool +MachThread::GetIdentifierInfo () +{ +#ifdef THREAD_IDENTIFIER_INFO_COUNT + if (m_ident_info.thread_id == 0) + { + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + return ::thread_info (ThreadID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count) == KERN_SUCCESS; + } +#endif + + return false; +} + + +const char * +MachThread::GetName () +{ + if (GetIdentifierInfo ()) + { + int len = ::proc_pidinfo (m_process->ProcessID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo)); + + if (len && m_proc_threadinfo.pth_name[0]) + return m_proc_threadinfo.pth_name; + } + return NULL; +} + + +// +//const char * +//MachThread::GetDispatchQueueName() +//{ +// if (GetIdentifierInfo ()) +// { +// if (m_ident_info.dispatch_qaddr == 0) +// return NULL; +// +// uint8_t memory_buffer[8]; +// DNBDataRef data(memory_buffer, sizeof(memory_buffer), false); +// ModuleSP module_sp(GetProcess()->GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib"))); +// if (module_sp.get() == NULL) +// return NULL; +// +// lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; +// const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData); +// if (dispatch_queue_offsets_symbol) +// dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(GetProcess()); +// +// if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) +// return NULL; +// +// // Excerpt from src/queue_private.h +// struct dispatch_queue_offsets_s +// { +// uint16_t dqo_version; +// uint16_t dqo_label; +// uint16_t dqo_label_size; +// } dispatch_queue_offsets; +// +// +// if (GetProcess()->ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets)) == sizeof(dispatch_queue_offsets)) +// { +// uint32_t data_offset = 0; +// if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) +// { +// if (GetProcess()->ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize()) == data.GetAddressByteSize()) +// { +// data_offset = 0; +// lldb::addr_t queue_addr = data.GetAddress(&data_offset); +// lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; +// const size_t chunk_size = 32; +// uint32_t label_pos = 0; +// m_dispatch_queue_name.resize(chunk_size, '\0'); +// while (1) +// { +// size_t bytes_read = GetProcess()->ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size); +// +// if (bytes_read <= 0) +// break; +// +// if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos) +// break; +// label_pos += bytes_read; +// } +// m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0')); +// } +// } +// } +// } +// +// if (m_dispatch_queue_name.empty()) +// return NULL; +// return m_dispatch_queue_name.c_str(); +//} diff --git a/lldb/tools/debugserver/source/MacOSX/MachThread.h b/lldb/tools/debugserver/source/MacOSX/MachThread.h new file mode 100644 index 000000000000..2bf5ff2c3bbf --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachThread.h @@ -0,0 +1,124 @@ +//===-- MachThread.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/19/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachThread_h__ +#define __MachThread_h__ + +#include +#include +#include // for std::tr1::shared_ptr + +#include +#include +#include +#include + +#include "PThreadCondition.h" +#include "PThreadMutex.h" +#include "MachException.h" +#include "DNBArch.h" +#include "DNBRegisterInfo.h" + +class DNBBreakpoint; +class MachProcess; + +class MachThread +{ +public: + + MachThread (MachProcess *process, thread_t thread = 0); + ~MachThread (); + + MachProcess * Process() { return m_process; } + const MachProcess * + Process() const { return m_process; } + nub_process_t ProcessID() const; + void Dump(uint32_t index); + thread_t ThreadID() const { return m_tid; } + thread_t InferiorThreadID() const; + + uint32_t SequenceID() const { return m_seq_id; } + static bool ThreadIDIsValid(thread_t thread); + uint32_t Resume(); + uint32_t Suspend(); + uint32_t SuspendCount() const { return m_suspendCount; } + bool RestoreSuspendCount(); + + bool GetRegisterState(int flavor, bool force); + bool SetRegisterState(int flavor); + uint64_t GetPC(uint64_t failValue = INVALID_NUB_ADDRESS); // Get program counter + bool SetPC(uint64_t value); // Set program counter + uint64_t GetSP(uint64_t failValue = INVALID_NUB_ADDRESS); // Get stack pointer + + nub_break_t CurrentBreakpoint() const { return m_breakID; } + void SetCurrentBreakpoint(nub_break_t breakID) { m_breakID = breakID; } + uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *breakpoint); + uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *watchpoint); + bool DisableHardwareBreakpoint (const DNBBreakpoint *breakpoint); + bool DisableHardwareWatchpoint (const DNBBreakpoint *watchpoint); + + nub_state_t GetState(); + void SetState(nub_state_t state); + + void ThreadWillResume (const DNBThreadResumeAction *thread_action); + bool ShouldStop(bool &step_more); + bool IsStepping(); + bool ThreadDidStop(); + bool NotifyException(MachException::Data& exc); + const MachException::Data& GetStopException() { return m_stop_exception; } + + uint32_t GetNumRegistersInSet(int regSet) const; + const char * GetRegisterSetName(int regSet) const; + const DNBRegisterInfo * + GetRegisterInfo(int regSet, int regIndex) const; + void DumpRegisterState(int regSet); + const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets ) const; + bool GetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value ); + bool SetRegisterValue ( uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value ); + nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len); + nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len); + void NotifyBreakpointChanged (const DNBBreakpoint *bp); + const char * GetBasicInfoAsString () const; + const char * GetName (); +protected: + static bool GetBasicInfo(thread_t threadID, struct thread_basic_info *basic_info); + + bool + GetIdentifierInfo (); + +// const char * +// GetDispatchQueueName(); +// + MachProcess * m_process; // The process that owns this thread + thread_t m_tid; // The thread port for this thread + uint32_t m_seq_id; // A Sequential ID that increments with each new thread + nub_state_t m_state; // The state of our process + PThreadMutex m_state_mutex; // Multithreaded protection for m_state + nub_break_t m_breakID; // Breakpoint that this thread is (stopped)/was(running) at (NULL for none) + struct thread_basic_info m_basicInfo; // Basic information for a thread used to see if a thread is valid + uint32_t m_suspendCount; // The current suspend count + MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped + DNBArch m_arch; // Arch specific information for register state and more + std::vector m_regSets; // Register set information for this thread +#ifdef THREAD_IDENTIFIER_INFO_COUNT + thread_identifier_info_data_t m_ident_info; + struct proc_threadinfo m_proc_threadinfo; + std::string m_dispatch_queue_name; +#endif + +}; + +typedef std::tr1::shared_ptr MachThreadSP; + +#endif diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp new file mode 100644 index 000000000000..b1ccc74f8e61 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp @@ -0,0 +1,432 @@ +//===-- MachThreadList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/19/07. +// +//===----------------------------------------------------------------------===// + +#include "MachThreadList.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "MachProcess.h" + +MachThreadList::MachThreadList() : + m_threads(), + m_threads_mutex(PTHREAD_MUTEX_RECURSIVE) +{ +} + +MachThreadList::~MachThreadList() +{ +} + +// Not thread safe, must lock m_threads_mutex prior to using this function. +uint32_t +MachThreadList::GetThreadIndexByID(thread_t tid) const +{ + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->ThreadID() == tid) + return idx; + } + return ~((uint32_t)0); +} + +nub_state_t +MachThreadList::GetState(thread_t tid) +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->GetState(); + return eStateInvalid; +} + +const char * +MachThreadList::GetName (thread_t tid) +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->GetName(); + return NULL; +} + +nub_thread_t +MachThreadList::SetCurrentThread(thread_t tid) +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + m_current_thread = m_threads[idx]; + + if (m_current_thread.get()) + return m_current_thread->ThreadID(); + return INVALID_NUB_THREAD; +} + + +bool +MachThreadList::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->GetStopException().GetStopInfo(stop_info); + return false; +} + +bool +MachThreadList::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info) +{ + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + return ::thread_info (tid, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS; +} + +void +MachThreadList::DumpThreadStoppedReason(nub_thread_t tid) const +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + m_threads[idx]->GetStopException().DumpStopReason(); +} + +const char * +MachThreadList::GetThreadInfo(nub_thread_t tid) const +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->GetBasicInfoAsString(); + return NULL; +} + +bool +MachThreadList::GetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value ) const +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->GetRegisterValue(reg_set_idx, reg_idx, reg_value); + + return false; +} + +bool +MachThreadList::SetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value ) const +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->SetRegisterValue(reg_set_idx, reg_idx, reg_value); + + return false; +} + +nub_size_t +MachThreadList::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len) +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->GetRegisterContext (buf, buf_len); + return 0; +} + +nub_size_t +MachThreadList::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len) +{ + uint32_t idx = GetThreadIndexByID(tid); + if (idx < m_threads.size()) + return m_threads[idx]->SetRegisterContext (buf, buf_len); + return 0; +} + +nub_size_t +MachThreadList::NumThreads() const +{ + return m_threads.size(); +} + +nub_thread_t +MachThreadList::ThreadIDAtIndex(nub_size_t idx) const +{ + if (idx < m_threads.size()) + return m_threads[idx]->ThreadID(); + return INVALID_NUB_THREAD; +} + +nub_thread_t +MachThreadList::CurrentThreadID ( ) +{ + MachThreadSP threadSP; + CurrentThread(threadSP); + if (threadSP.get()) + return threadSP->ThreadID(); + return INVALID_NUB_THREAD; +} + +bool +MachThreadList::NotifyException(MachException::Data& exc) +{ + uint32_t idx = GetThreadIndexByID(exc.thread_port); + if (idx < m_threads.size()) + { + m_threads[idx]->NotifyException(exc); + return true; + } + return false; +} + +/* +MachThreadList::const_iterator +MachThreadList::FindThreadByID(thread_t tid) const +{ + const_iterator pos; + const_iterator end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + if (pos->ThreadID() == tid) + return pos; + } + return NULL; +} +*/ +void +MachThreadList::Clear() +{ + m_threads.clear(); +} + +uint32_t +MachThreadList::UpdateThreadList(MachProcess *process, bool update) +{ + // locker will keep a mutex locked until it goes out of scope + DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u )", process->ProcessID(), update); + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + + if (m_threads.empty() || update) + { + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + task_t task = process->Task().TaskPort(); + DNBError err(::task_threads (task, &thread_list, &thread_list_count), DNBError::MachKernel); + + if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) + err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); + + if (err.Error() == KERN_SUCCESS && thread_list_count > 0) + { + MachThreadList::collection currThreads; + const size_t numOldThreads = m_threads.size(); + size_t idx; + // Iterator through the current thread list and see which threads + // we already have in our list (keep them), which ones we don't + // (add them), and which ones are not around anymore (remove them). + for (idx = 0; idx < thread_list_count; ++idx) + { + uint32_t existing_idx = 0; + if (numOldThreads > 0) + existing_idx = GetThreadIndexByID(thread_list[idx]); + if (existing_idx < numOldThreads) + { + // Keep the existing thread class + currThreads.push_back(m_threads[existing_idx]); + } + else + { + // We don't have this thread, lets add it. + MachThreadSP threadSP(new MachThread(process, thread_list[idx])); + currThreads.push_back(threadSP); + } + } + + m_threads.swap(currThreads); + m_current_thread.reset(); + + // Free the vm memory given to us by ::task_threads() + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t)); + ::vm_deallocate (::mach_task_self(), + (vm_address_t)thread_list, + thread_list_size); + } + } + return m_threads.size(); +} + + +void +MachThreadList::CurrentThread(MachThreadSP& threadSP) +{ + // locker will keep a mutex locked until it goes out of scope + PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); + if (m_current_thread.get() == NULL) + { + // Figure out which thread is going to be our current thread. + // This is currently done by finding the first thread in the list + // that has a valid exception. + const size_t num_threads = m_threads.size(); + size_t idx; + for (idx = 0; idx < num_threads; ++idx) + { + MachThread *thread = m_threads[idx].get(); + if (thread->GetStopException().IsValid()) + { + m_current_thread = m_threads[idx]; + break; + } + } + } + threadSP = m_current_thread; +} + +void +MachThreadList::GetRegisterState(int flavor, bool force) +{ + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->GetRegisterState(flavor, force); + } +} + +void +MachThreadList::SetRegisterState(int flavor) +{ + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->SetRegisterState(flavor); + } +} + +void +MachThreadList::Dump() const +{ + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->Dump(idx); + } +} + + +void +MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions) +{ + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + + for (idx = 0; idx < num_threads; ++idx) + { + MachThread *thread = m_threads[idx].get(); + + const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true); + // There must always be a thread action for every thread. + assert (thread_action); + thread->ThreadWillResume (thread_action); + } +} + +uint32_t +MachThreadList::ProcessDidStop(MachProcess *process) +{ + // Update our thread list + const uint32_t num_threads = UpdateThreadList(process, true); + uint32_t idx = 0; + for (idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->ThreadDidStop(); + } + return num_threads; +} + +//---------------------------------------------------------------------- +// Check each thread in our thread list to see if we should notify our +// client of the current halt in execution. +// +// Breakpoints can have callback functions associated with them than +// can return true to stop, or false to continue executing the inferior. +// +// RETURNS +// true if we should stop and notify our clients +// false if we should resume our child process and skip notification +//---------------------------------------------------------------------- +bool +MachThreadList::ShouldStop(bool &step_more) +{ + uint32_t should_stop = false; + const uint32_t num_threads = m_threads.size(); + uint32_t idx = 0; + for (idx = 0; !should_stop && idx < num_threads; ++idx) + { + should_stop = m_threads[idx]->ShouldStop(step_more); + } + return should_stop; +} + + +void +MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp) +{ + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + m_threads[idx]->NotifyBreakpointChanged(bp); + } +} + + +uint32_t +MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const +{ + if (bp != NULL) + { + uint32_t idx = GetThreadIndexByID(bp->ThreadID()); + if (idx < m_threads.size()) + return m_threads[idx]->EnableHardwareBreakpoint(bp); + } + return INVALID_NUB_HW_INDEX; +} + +bool +MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const +{ + if (bp != NULL) + { + uint32_t idx = GetThreadIndexByID(bp->ThreadID()); + if (idx < m_threads.size()) + return m_threads[idx]->DisableHardwareBreakpoint(bp); + } + return false; +} + +uint32_t +MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const +{ + if (wp != NULL) + { + uint32_t idx = GetThreadIndexByID(wp->ThreadID()); + if (idx < m_threads.size()) + return m_threads[idx]->EnableHardwareWatchpoint(wp); + } + return INVALID_NUB_HW_INDEX; +} + +bool +MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const +{ + if (wp != NULL) + { + uint32_t idx = GetThreadIndexByID(wp->ThreadID()); + if (idx < m_threads.size()) + return m_threads[idx]->DisableHardwareWatchpoint(wp); + } + return false; +} + + diff --git a/lldb/tools/debugserver/source/MacOSX/MachThreadList.h b/lldb/tools/debugserver/source/MacOSX/MachThreadList.h new file mode 100644 index 000000000000..b52a97b547bc --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachThreadList.h @@ -0,0 +1,71 @@ +//===-- MachThreadList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/19/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachThreadList_h__ +#define __MachThreadList_h__ + +#include "MachThread.h" + +class DNBThreadResumeActions; + +class MachThreadList +{ +public: + MachThreadList (); + ~MachThreadList (); + + void Clear (); + void Dump () const; + void GetRegisterState (int flavor, bool force); + void SetRegisterState (int flavor); + bool GetRegisterValue (nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value) const; + bool SetRegisterValue (nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value) const; + nub_size_t GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len); + nub_size_t SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len); + const char * GetThreadInfo (nub_thread_t tid) const; + void ProcessWillResume (MachProcess *process, const DNBThreadResumeActions &thread_actions); + uint32_t ProcessDidStop (MachProcess *process); + bool NotifyException (MachException::Data& exc); + bool ShouldStop (bool &step_more); + const char * GetName (thread_t tid); + nub_state_t GetState (thread_t tid); + nub_thread_t SetCurrentThread (thread_t tid); + bool GetThreadStoppedReason (nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const; + void DumpThreadStoppedReason (nub_thread_t tid) const; + bool GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info); + nub_size_t NumThreads () const; + nub_thread_t ThreadIDAtIndex (nub_size_t idx) const; + nub_thread_t CurrentThreadID (); + uint32_t GetThreadIndexByID (thread_t tid) const; + void CurrentThread (MachThreadSP& threadSP); + void NotifyBreakpointChanged (const DNBBreakpoint *bp); + uint32_t EnableHardwareBreakpoint (const DNBBreakpoint *bp) const; + bool DisableHardwareBreakpoint (const DNBBreakpoint *bp) const; + uint32_t EnableHardwareWatchpoint (const DNBBreakpoint *wp) const; + bool DisableHardwareWatchpoint (const DNBBreakpoint *wp) const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + uint32_t UpdateThreadList (MachProcess *process, bool update); +// const_iterator FindThreadByID (thread_t tid) const; + + collection m_threads; + PThreadMutex m_threads_mutex; + MachThreadSP m_current_thread; +}; + +#endif // #ifndef __MachThreadList_h__ + diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp new file mode 100644 index 000000000000..eb7e10746e23 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp @@ -0,0 +1,186 @@ +//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#include "MachVMMemory.h" +#include "MachVMRegion.h" +#include "DNBLog.h" +#include + +MachVMMemory::MachVMMemory() : + m_page_size (kInvalidPageSize), + m_err (0) +{ +} + +MachVMMemory::~MachVMMemory() +{ +} + +nub_size_t +MachVMMemory::PageSize() +{ + if (m_page_size == kInvalidPageSize) + { + m_err = ::host_page_size( ::mach_host_self(), &m_page_size); + if (m_err.Fail()) + m_page_size = 0; + } + return m_page_size; +} + +nub_size_t +MachVMMemory::MaxBytesLeftInPage(nub_addr_t addr, nub_size_t count) +{ + const nub_size_t page_size = PageSize(); + if (page_size > 0) + { + nub_size_t page_offset = (addr % page_size); + nub_size_t bytes_left_in_page = page_size - page_offset; + if (count > bytes_left_in_page) + count = bytes_left_in_page; + } + return count; +} + +nub_size_t +MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count) +{ + if (data == NULL || data_count == 0) + return 0; + + nub_size_t total_bytes_read = 0; + nub_addr_t curr_addr = address; + uint8_t *curr_data = (uint8_t*)data; + while (total_bytes_read < data_count) + { + mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read); + mach_msg_type_number_t curr_bytes_read = 0; + vm_offset_t vm_memory = NULL; + m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read); + if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail()) + m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i )", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read); + + if (m_err.Success()) + { + if (curr_bytes_read != curr_size) + { + if (DNBLogCheckLogBit(LOG_MEMORY)) + m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size); + } + ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read); + ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read); + total_bytes_read += curr_bytes_read; + curr_addr += curr_bytes_read; + curr_data += curr_bytes_read; + } + else + { + break; + } + } + return total_bytes_read; +} + + +nub_size_t +MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count) +{ + MachVMRegion vmRegion(task); + + nub_size_t total_bytes_written = 0; + nub_addr_t curr_addr = address; + const uint8_t *curr_data = (const uint8_t*)data; + + + while (total_bytes_written < data_count) + { + if (vmRegion.GetRegionForAddress(curr_addr)) + { + mach_vm_size_t curr_data_count = data_count - total_bytes_written; + mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr); + if (region_bytes_left == 0) + { + break; + } + if (curr_data_count > region_bytes_left) + curr_data_count = region_bytes_left; + + if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE)) + { + nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count); + if (bytes_written <= 0) + { + // Error should have already be posted by WriteRegion... + break; + } + else + { + total_bytes_written += bytes_written; + curr_addr += bytes_written; + curr_data += bytes_written; + } + } + else + { + DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count)); + break; + } + } + else + { + DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address); + break; + } + } + + return total_bytes_written; +} + + +nub_size_t +MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count) +{ + if (data == NULL || data_count == 0) + return 0; + + nub_size_t total_bytes_written = 0; + nub_addr_t curr_addr = address; + const uint8_t *curr_data = (const uint8_t*)data; + while (total_bytes_written < data_count) + { + mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written); + m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count); + if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail()) + m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count); + +#if !defined (__i386__) && !defined (__x86_64__) + vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH; + + m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value); + if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail()) + m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count); +#endif + + if (m_err.Success()) + { + total_bytes_written += curr_data_count; + curr_addr += curr_data_count; + curr_data += curr_data_count; + } + else + { + break; + } + } + return total_bytes_written; +} diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h new file mode 100644 index 000000000000..5635186854a5 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h @@ -0,0 +1,40 @@ +//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachVMMemory_h__ +#define __MachVMMemory_h__ + +#include "DNBDefs.h" +#include "DNBError.h" +#include + +class MachVMMemory +{ +public: + enum { kInvalidPageSize = ~0 }; + MachVMMemory(); + ~MachVMMemory(); + nub_size_t Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count); + nub_size_t Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count); + nub_size_t PageSize(); + +protected: + nub_size_t MaxBytesLeftInPage(nub_addr_t addr, nub_size_t count); + + nub_size_t WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count); + vm_size_t m_page_size; + DNBError m_err; +}; + + +#endif // #ifndef __MachVMMemory_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp new file mode 100644 index 000000000000..6299cf4179f4 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp @@ -0,0 +1,179 @@ +//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#include "MachVMRegion.h" +#include +#include "DNBLog.h" +#include + +MachVMRegion::MachVMRegion(task_t task) : + m_task(task), + m_addr(INVALID_NUB_ADDRESS), + m_err(), + m_start(INVALID_NUB_ADDRESS), + m_size(0), + m_depth(-1), + m_curr_protection(0), + m_protection_addr(INVALID_NUB_ADDRESS), + m_protection_size(0) +{ + memset(&m_data, 0, sizeof(m_data)); +} + +MachVMRegion::~MachVMRegion() +{ + // Restore any original protections and clear our vars + Clear(); +} + +void +MachVMRegion::Clear() +{ + RestoreProtections(); + m_addr = INVALID_NUB_ADDRESS; + m_err.Clear(); + m_start = INVALID_NUB_ADDRESS; + m_size = 0; + m_depth = -1; + memset(&m_data, 0, sizeof(m_data)); + m_curr_protection = 0; + m_protection_addr = INVALID_NUB_ADDRESS; + m_protection_size = 0; +} + +bool +MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot) +{ + if (ContainsAddress(addr)) + { + mach_vm_size_t prot_size = size; + mach_vm_address_t end_addr = EndAddress(); + if (prot_size > (end_addr - addr)) + prot_size = end_addr - addr; + + if (prot_size > 0) + { + if (prot == (m_curr_protection & VM_PROT_ALL)) + { + DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE, "MachVMRegion::%s: protections (%u) already sufficient for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, prot, m_task, (uint64_t)addr); + // Protections are already set as requested... + return true; + } + else + { + m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot); + if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS)) + m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot); + if (m_err.Fail()) + { + // Try again with the ability to create a copy on write region + m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot | VM_PROT_COPY); + if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail()) + m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot | VM_PROT_COPY); + } + if (m_err.Success()) + { + m_curr_protection = prot; + m_protection_addr = addr; + m_protection_size = prot_size; + return true; + } + } + } + else + { + DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE, "%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, m_task, (uint64_t)addr); + } + } + return false; +} + +bool +MachVMRegion::RestoreProtections() +{ + if (m_curr_protection != m_data.protection && m_protection_size > 0) + { + m_err = ::mach_vm_protect (m_task, m_protection_addr, m_protection_size, 0, m_data.protection); + if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail()) + m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)m_protection_addr, (uint64_t)m_protection_size, 0, m_data.protection); + if (m_err.Success()) + { + m_protection_size = 0; + m_protection_addr = INVALID_NUB_ADDRESS; + m_curr_protection = m_data.protection; + return true; + } + } + else + { + m_err.Clear(); + return true; + } + + return false; +} + +bool +MachVMRegion::GetRegionForAddress(nub_addr_t addr) +{ + // Restore any original protections and clear our vars + Clear(); + m_addr = addr; + m_start = addr; + m_depth = 1024; + mach_msg_type_number_t info_size = kRegionInfoSize; + assert(sizeof(info_size) == 4); + m_err = ::mach_vm_region_recurse (m_task, &m_start, &m_size, &m_depth, (vm_region_recurse_info_t)&m_data, &info_size); + if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail()) + m_err.LogThreaded("::mach_vm_region_recurse ( task = 0x%4.4x, address => 0x%8.8llx, size => %llu, nesting_depth => %d, info => %p, infoCnt => %d) addr = 0x%8.8llx ", m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth, &m_data, info_size, (uint64_t)addr); + if (m_err.Fail()) + { + return false; + } + else + { + if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS)) + { + DNBLogThreaded("info = { prot = %u, " + "max_prot = %u, " + "inheritance = 0x%8.8x, " + "offset = 0x%8.8llx, " + "user_tag = 0x%8.8x, " + "ref_count = %u, " + "shadow_depth = %u, " + "ext_pager = %u, " + "share_mode = %u, " + "is_submap = %d, " + "behavior = %d, " + "object_id = 0x%8.8x, " + "user_wired_count = 0x%4.4x }", + m_data.protection, + m_data.max_protection, + m_data.inheritance, + (uint64_t)m_data.offset, + m_data.user_tag, + m_data.ref_count, + m_data.shadow_depth, + m_data.external_pager, + m_data.share_mode, + m_data.is_submap, + m_data.behavior, + m_data.object_id, + m_data.user_wired_count); + } + } + + m_curr_protection = m_data.protection; + + return true; +} diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h new file mode 100644 index 000000000000..617e221a57ed --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h @@ -0,0 +1,67 @@ +//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachVMRegion_h__ +#define __MachVMRegion_h__ + +#include "DNBDefs.h" +#include "DNBError.h" +#include + +class MachVMRegion +{ +public: + MachVMRegion(task_t task); + ~MachVMRegion(); + + void Clear(); + mach_vm_address_t StartAddress() const { return m_start; } + mach_vm_address_t EndAddress() const { return m_start + m_size; } + mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const + { + if (ContainsAddress(addr)) + return m_size - (addr - m_start); + else + return 0; + } + bool ContainsAddress(mach_vm_address_t addr) const + { + return addr >= StartAddress() && addr < EndAddress(); + } + + bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot); + bool RestoreProtections(); + bool GetRegionForAddress(nub_addr_t addr); +protected: +#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64) + typedef vm_region_submap_short_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; +#else + typedef vm_region_submap_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 }; +#endif + + task_t m_task; + mach_vm_address_t m_addr; + DNBError m_err; + mach_vm_address_t m_start; + mach_vm_size_t m_size; + natural_t m_depth; + RegionInfo m_data; + vm_prot_t m_curr_protection; // The current, possibly modified protections. Original value is saved in m_data.protections. + mach_vm_address_t m_protection_addr; // The start address at which protections were changed + mach_vm_size_t m_protection_size; // The size of memory that had its protections changed + +}; + +#endif // #ifndef __MachVMRegion_h__ diff --git a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp new file mode 100644 index 000000000000..6eec80cfd7a2 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp @@ -0,0 +1,2610 @@ +//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#if defined (__arm__) + +#include "MacOSX/arm/DNBArchImpl.h" +#include "MacOSX/MachProcess.h" +#include "MacOSX/MachThread.h" +#include "DNBBreakpoint.h" +#include "DNBLog.h" +#include "DNBRegisterInfo.h" +#include "DNB.h" + +#include + +// BCR address match type +#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21)) +#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21)) +#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21)) +#define BCR_M_RESERVED ((uint32_t)(3u << 21)) + +// Link a BVR/BCR or WVR/WCR pair to another +#define E_ENABLE_LINKING ((uint32_t)(1u << 20)) + +// Byte Address Select +#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5)) +#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6)) +#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7)) +#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8)) +#define BAS_IMVA_0_1 ((uint32_t)(3u << 5)) +#define BAS_IMVA_2_3 ((uint32_t)(3u << 7)) +#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5)) + +// Break only in priveleged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +//#define DNB_ARCH_MACH_ARM_DEBUG_SW_STEP 1 + +static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; +static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + +// ARM constants used during decoding +#define REG_RD 0 +#define LDM_REGLIST 1 +#define PC_REG 15 +#define PC_REGLIST_BIT 0x8000 + +// ARM conditions +#define COND_EQ 0x0 +#define COND_NE 0x1 +#define COND_CS 0x2 +#define COND_HS 0x2 +#define COND_CC 0x3 +#define COND_LO 0x3 +#define COND_MI 0x4 +#define COND_PL 0x5 +#define COND_VS 0x6 +#define COND_VC 0x7 +#define COND_HI 0x8 +#define COND_LS 0x9 +#define COND_GE 0xA +#define COND_LT 0xB +#define COND_GT 0xC +#define COND_LE 0xD +#define COND_AL 0xE +#define COND_UNCOND 0xF + +#define MASK_CPSR_T (1u << 5) +#define MASK_CPSR_J (1u << 24) + +#define MNEMONIC_STRING_SIZE 32 +#define OPERAND_STRING_SIZE 128 + +const uint8_t * const +DNBArchMachARM::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + switch (byte_size) + { + case 2: return g_thumb_breakpooint_opcode; + case 4: return g_arm_breakpoint_opcode; + } + return NULL; +} + +uint32_t +DNBArchMachARM::GetCPUType() +{ + return CPU_TYPE_ARM; +} + +uint64_t +DNBArchMachARM::GetPC(uint64_t failValue) +{ + // Get program counter + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.gpr.__pc; + return failValue; +} + +kern_return_t +DNBArchMachARM::SetPC(uint64_t value) +{ + // Get program counter + kern_return_t err = GetGPRState(false); + if (err == KERN_SUCCESS) + { + m_state.gpr.__pc = value; + err = SetGPRState(); + } + return err == KERN_SUCCESS; +} + +uint64_t +DNBArchMachARM::GetSP(uint64_t failValue) +{ + // Get stack pointer + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.gpr.__sp; + return failValue; +} + +kern_return_t +DNBArchMachARM::GetGPRState(bool force) +{ + int set = e_regSetGPR; + // Check if we have valid cached registers + if (!force && m_state.GetError(set, Read) == KERN_SUCCESS) + return KERN_SUCCESS; + + // Read the registers from our thread + mach_msg_type_number_t count = ARM_THREAD_STATE_COUNT; + kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_THREAD_STATE, (thread_state_t)&m_state.gpr, &count); + uint32_t *r = &m_state.gpr.__r[0]; + DNBLogThreadedIf(LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x regs r0=%8.8x r1=%8.8x r2=%8.8x r3=%8.8x r4=%8.8x r5=%8.8x r6=%8.8x r7=%8.8x r8=%8.8x r9=%8.8x r10=%8.8x r11=%8.8x s12=%8.8x sp=%8.8x lr=%8.8x pc=%8.8x cpsr=%8.8x", m_thread->ThreadID(), ARM_THREAD_STATE, ARM_THREAD_STATE_COUNT, kret, + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15], r[16]); + m_state.SetError(set, Read, kret); + return kret; +} + +kern_return_t +DNBArchMachARM::GetVFPState(bool force) +{ + int set = e_regSetVFP; + // Check if we have valid cached registers + if (!force && m_state.GetError(set, Read) == KERN_SUCCESS) + return KERN_SUCCESS; + + // Read the registers from our thread + mach_msg_type_number_t count = ARM_VFP_STATE_COUNT; + kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_VFP_STATE, (thread_state_t)&m_state.vfp, &count); + m_state.SetError(set, Read, kret); + return kret; +} + +kern_return_t +DNBArchMachARM::GetEXCState(bool force) +{ + int set = e_regSetEXC; + // Check if we have valid cached registers + if (!force && m_state.GetError(set, Read) == KERN_SUCCESS) + return KERN_SUCCESS; + + // Read the registers from our thread + mach_msg_type_number_t count = ARM_EXCEPTION_STATE_COUNT; + kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.exc, &count); + m_state.SetError(set, Read, kret); + return kret; +} + +static void +DumpDBGState(const arm_debug_state_t& dbg) +{ + uint32_t i = 0; + for (i=0; i<16; i++) + DNBLogThreadedIf(LOG_STEP, "BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }", + i, i, dbg.__bvr[i], dbg.__bcr[i], + i, i, dbg.__wvr[i], dbg.__wcr[i]); +} + +kern_return_t +DNBArchMachARM::GetDBGState(bool force) +{ + int set = e_regSetDBG; + + // Check if we have valid cached registers + if (!force && m_state.GetError(set, Read) == KERN_SUCCESS) + return KERN_SUCCESS; + + // Read the registers from our thread + mach_msg_type_number_t count = ARM_DEBUG_STATE_COUNT; + kern_return_t kret = ::thread_get_state(m_thread->ThreadID(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, &count); + m_state.SetError(set, Read, kret); + return kret; +} + +kern_return_t +DNBArchMachARM::SetGPRState() +{ + int set = e_regSetGPR; + kern_return_t kret = ::thread_set_state(m_thread->ThreadID(), ARM_THREAD_STATE, (thread_state_t)&m_state.gpr, ARM_THREAD_STATE_COUNT); + m_state.SetError(set, Write, kret); // Set the current write error for this register set + m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently + return kret; // Return the error code +} + +kern_return_t +DNBArchMachARM::SetVFPState() +{ + int set = e_regSetVFP; + kern_return_t kret = ::thread_set_state (m_thread->ThreadID(), ARM_VFP_STATE, (thread_state_t)&m_state.vfp, ARM_VFP_STATE_COUNT); + m_state.SetError(set, Write, kret); // Set the current write error for this register set + m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently + return kret; // Return the error code +} + +kern_return_t +DNBArchMachARM::SetEXCState() +{ + int set = e_regSetEXC; + kern_return_t kret = ::thread_set_state (m_thread->ThreadID(), ARM_EXCEPTION_STATE, (thread_state_t)&m_state.exc, ARM_EXCEPTION_STATE_COUNT); + m_state.SetError(set, Write, kret); // Set the current write error for this register set + m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently + return kret; // Return the error code +} + +kern_return_t +DNBArchMachARM::SetDBGState() +{ + int set = e_regSetDBG; + kern_return_t kret = ::thread_set_state (m_thread->ThreadID(), ARM_DEBUG_STATE, (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT); + m_state.SetError(set, Write, kret); // Set the current write error for this register set + m_state.InvalidateRegisterSetState(set); // Invalidate the current register state in case registers are read back differently + return kret; // Return the error code +} + +void +DNBArchMachARM::ThreadWillResume() +{ + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread->IsStepping()) + { + bool step_handled = false; + // This is the primary thread, let the arch do anything it needs + if (NumSupportedHardwareBreakpoints() > 0) + { +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + bool half_step = m_hw_single_chained_step_addr != INVALID_NUB_ADDRESS; +#endif + step_handled = EnableHardwareSingleStep(true) == KERN_SUCCESS; +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + if (!half_step) + step_handled = false; +#endif + } + + if (!step_handled) + { + SetSingleStepSoftwareBreakpoints(); + } + } +} + +bool +DNBArchMachARM::ThreadDidStop() +{ + bool success = true; + + m_state.InvalidateRegisterSetState (e_regSetALL); + + // Are we stepping a single instruction? + if (GetGPRState(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread->IsStepping()) + { +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + // Hardware single step must work if we are going to test software + // single step functionality + assert(success); + if (m_hw_single_chained_step_addr == INVALID_NUB_ADDRESS && m_sw_single_step_next_pc != INVALID_NUB_ADDRESS) + { + uint32_t sw_step_next_pc = m_sw_single_step_next_pc & 0xFFFFFFFEu; + bool sw_step_next_pc_is_thumb = (m_sw_single_step_next_pc & 1) != 0; + bool actual_next_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0; + if (m_state.gpr.__pc != sw_step_next_pc) + { + DNBLogError("curr pc = 0x%8.8x - calculated single step target PC was incorrect: 0x%8.8x != 0x%8.8x", m_state.gpr.__pc, sw_step_next_pc, m_state.gpr.__pc); + exit(1); + } + if (actual_next_pc_is_thumb != sw_step_next_pc_is_thumb) + { + DNBLogError("curr pc = 0x%8.8x - calculated single step calculated mode mismatch: sw single mode = %s != %s", + m_state.gpr.__pc, + actual_next_pc_is_thumb ? "Thumb" : "ARM", + sw_step_next_pc_is_thumb ? "Thumb" : "ARM"); + exit(1); + } + m_sw_single_step_next_pc = INVALID_NUB_ADDRESS; + } +#else + // Are we software single stepping? + if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_break_id) || m_sw_single_step_itblock_break_count) + { + // Remove any software single stepping breakpoints that we have set + + // Do we have a normal software single step breakpoint? + if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_break_id)) + { + DNBLogThreadedIf(LOG_STEP, "%s: removing software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_break_id); + success = m_thread->Process()->DisableBreakpoint(m_sw_single_step_break_id, true); + m_sw_single_step_break_id = INVALID_NUB_BREAK_ID; + } + + // Do we have any Thumb IT breakpoints? + if (m_sw_single_step_itblock_break_count > 0) + { + // See if we hit one of our Thumb IT breakpoints? + DNBBreakpoint *step_bp = m_thread->Process()->Breakpoints().FindByAddress(m_state.gpr.__pc); + + if (step_bp) + { + // We did hit our breakpoint, tell the breakpoint it was + // hit so that it can run its callback routine and fixup + // the PC. + DNBLogThreadedIf(LOG_STEP, "%s: IT software single step breakpoint hit (breakID=%u)", __FUNCTION__, step_bp->GetID()); + step_bp->BreakpointHit(m_thread->Process()->ProcessID(), m_thread->ThreadID()); + } + + // Remove all Thumb IT breakpoints + for (int i = 0; i < m_sw_single_step_itblock_break_count; i++) + { + if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + { + DNBLogThreadedIf(LOG_STEP, "%s: removing IT software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_itblock_break_id[i]); + success = m_thread->Process()->DisableBreakpoint(m_sw_single_step_itblock_break_id[i], true); + m_sw_single_step_itblock_break_id[i] = INVALID_NUB_BREAK_ID; + } + } + m_sw_single_step_itblock_break_count = 0; + + // Decode instructions up to the current PC to ensure the internal decoder state is valid for the IT block + // The decoder has to decode each instruction in the IT block even if it is not executed so that + // the fields are correctly updated + DecodeITBlockInstructions(m_state.gpr.__pc); + } + + } + else + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; +#endif + } + else + { + // The MachThread will automatically restore the suspend count + // in ThreadDidStop(), so we don't need to do anything here if + // we weren't the primary thread the last time + } + } + return success; +} + +bool +DNBArchMachARM::StepNotComplete () +{ + if (m_hw_single_chained_step_addr != INVALID_NUB_ADDRESS) + { + kern_return_t kret = KERN_INVALID_ARGUMENT; + kret = GetGPRState(false); + if (kret == KERN_SUCCESS) + { + if (m_state.gpr.__pc == m_hw_single_chained_step_addr) + { + DNBLogThreadedIf(LOG_STEP, "Need to step some more at 0x%8.8x", m_hw_single_chained_step_addr); + return true; + } + } + } + + m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS; + return false; +} + + +void +DNBArchMachARM::DecodeITBlockInstructions(nub_addr_t curr_pc) + +{ + uint16_t opcode16; + uint32_t opcode32; + nub_addr_t next_pc_in_itblock; + nub_addr_t pc_in_itblock = m_last_decode_pc; + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc); + + // Decode IT block instruction from the instruction following the m_last_decoded_instruction at + // PC m_last_decode_pc upto and including the instruction at curr_pc + if (m_thread->Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2) + { + opcode32 = opcode16; + pc_in_itblock += 2; + // Check for 32 bit thumb opcode and read the upper 16 bits if needed + if (((opcode32 & 0xE000) == 0xE000) && opcode32 & 0x1800) + { + // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for + // a 32 bit Thumb opcode + // Read bits 31:16 of a 32 bit Thumb opcode + if (m_thread->Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2) + { + pc_in_itblock += 2; + // 32 bit thumb opcode + opcode32 = (opcode32 << 16) | opcode16; + } + else + { + DNBLogError("%s: Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", __FUNCTION__, pc_in_itblock); + } + } + } + else + { + DNBLogError("%s: Error reading 16-bit Thumb instruction at pc=0x%8.8x", __FUNCTION__, pc_in_itblock); + } + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: pc_in_itblock=0x%8.8x, curr_pc=0x%8.8x", __FUNCTION__, pc_in_itblock, curr_pc); + + next_pc_in_itblock = pc_in_itblock; + while (next_pc_in_itblock <= curr_pc) + { + arm_error_t decodeError; + + m_last_decode_pc = pc_in_itblock; + decodeError = DecodeInstructionUsingDisassembler(pc_in_itblock, m_state.gpr.__cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc_in_itblock); + + pc_in_itblock = next_pc_in_itblock; + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: next_pc_in_itblock=0x%8.8x", __FUNCTION__, next_pc_in_itblock); + } +} + + +// Set the single step bit in the processor status register. +kern_return_t +DNBArchMachARM::EnableHardwareSingleStep (bool enable) +{ + DNBError err; + DNBLogThreadedIf(LOG_STEP, "%s( enable = %d )", __FUNCTION__, enable); + + err = GetGPRState(false); + + if (err.Fail()) + { + err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__); + return err.Error(); + } + + err = GetDBGState(false); + + if (err.Fail()) + { + err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__); + return err.Error(); + } + + const uint32_t i = 0; + if (enable) + { + m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS; + + // Save our previous state + m_dbg_save = m_state.dbg; + // Set a breakpoint that will stop when the PC doesn't match the current one! + m_state.dbg.__bvr[i] = m_state.gpr.__pc & 0xFFFFFFFCu; // Set the current PC as the breakpoint address + m_state.dbg.__bcr[i] = BCR_M_IMVA_MISMATCH | // Stop on address mismatch + S_USER | // Stop only in user mode + BCR_ENABLE; // Enable this breakpoint + if (m_state.gpr.__cpsr & 0x20) + { + // Thumb breakpoint + if (m_state.gpr.__pc & 2) + m_state.dbg.__bcr[i] |= BAS_IMVA_2_3; + else + m_state.dbg.__bcr[i] |= BAS_IMVA_0_1; + + uint16_t opcode; + if (sizeof(opcode) == m_thread->Process()->Task().ReadMemory(m_state.gpr.__pc, sizeof(opcode), &opcode)) + { + if (((opcode & 0xE000) == 0xE000) && opcode & 0x1800) + { + // 32 bit thumb opcode... + if (m_state.gpr.__pc & 2) + { + // We can't take care of a 32 bit thumb instruction single step + // with just IVA mismatching. We will need to chain an extra + // hardware single step in order to complete this single step... + m_hw_single_chained_step_addr = m_state.gpr.__pc + 2; + } + else + { + // Extend the number of bits to ignore for the mismatch + m_state.dbg.__bcr[i] |= BAS_IMVA_ALL; + } + } + } + } + else + { + // ARM breakpoint + m_state.dbg.__bcr[i] |= BAS_IMVA_ALL; // Stop when any address bits change + } + + DNBLogThreadedIf(LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__, i, m_state.dbg.__bvr[i], i, m_state.dbg.__bcr[i]); + + for (uint32_t j=i+1; j<16; ++j) + { + // Disable all others + m_state.dbg.__bvr[j] = 0; + m_state.dbg.__bcr[j] = 0; + } + } + else + { + // Just restore the state we had before we did single stepping + m_state.dbg = m_dbg_save; + } + + return SetDBGState(); +} + +// return 1 if bit "BIT" is set in "value" +static inline uint32_t bit(uint32_t value, uint32_t bit) +{ + return (value >> bit) & 1u; +} + +// return the bitfield "value[msbit:lsbit]". +static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit) +{ + assert(msbit >= lsbit); + uint32_t shift_left = sizeof(value) * 8 - 1 - msbit; + value <<= shift_left; // shift anything above the msbit off of the unsigned edge + value >>= shift_left + lsbit; // shift it back again down to the lsbit (including undoing any shift from above) + return value; // return our result +} + +bool +DNBArchMachARM::ConditionPassed(uint8_t condition, uint32_t cpsr) +{ + uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag + uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag + uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag + uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag + + switch (condition) { + case COND_EQ: // (0x0) + if (cpsr_z == 1) return true; + break; + case COND_NE: // (0x1) + if (cpsr_z == 0) return true; + break; + case COND_CS: // (0x2) + if (cpsr_c == 1) return true; + break; + case COND_CC: // (0x3) + if (cpsr_c == 0) return true; + break; + case COND_MI: // (0x4) + if (cpsr_n == 1) return true; + break; + case COND_PL: // (0x5) + if (cpsr_n == 0) return true; + break; + case COND_VS: // (0x6) + if (cpsr_v == 1) return true; + break; + case COND_VC: // (0x7) + if (cpsr_v == 0) return true; + break; + case COND_HI: // (0x8) + if ((cpsr_c == 1) && (cpsr_z == 0)) return true; + break; + case COND_LS: // (0x9) + if ((cpsr_c == 0) || (cpsr_z == 1)) return true; + break; + case COND_GE: // (0xA) + if (cpsr_n == cpsr_v) return true; + break; + case COND_LT: // (0xB) + if (cpsr_n != cpsr_v) return true; + break; + case COND_GT: // (0xC) + if ((cpsr_z == 0) && (cpsr_n == cpsr_v)) return true; + break; + case COND_LE: // (0xD) + if ((cpsr_z == 1) || (cpsr_n != cpsr_v)) return true; + break; + default: + return true; + break; + } + + return false; +} + +bool +DNBArchMachARM::ComputeNextPC(nub_addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, nub_addr_t *targetPC) +{ + nub_addr_t myTargetPC, addressWherePCLives; + pid_t mypid; + + uint32_t cpsr_c = bit(m_state.gpr.__cpsr, 29); // Carry condition code flag + + uint32_t firstOperand=0, secondOperand=0, shiftAmount=0, secondOperandAfterShift=0, immediateValue=0; + uint32_t halfwords=0, baseAddress=0, immediateOffset=0, addressOffsetFromRegister=0, addressOffsetFromRegisterAfterShift; + uint32_t baseAddressIndex=INVALID_NUB_HW_INDEX; + uint32_t firstOperandIndex=INVALID_NUB_HW_INDEX; + uint32_t secondOperandIndex=INVALID_NUB_HW_INDEX; + uint32_t addressOffsetFromRegisterIndex=INVALID_NUB_HW_INDEX; + uint32_t shiftRegisterIndex=INVALID_NUB_HW_INDEX; + uint16_t registerList16, registerList16NoPC; + uint8_t registerList8; + uint32_t numRegistersToLoad=0; + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: instruction->code=%d", __FUNCTION__, decodedInstruction.instruction->code); + + // Get the following in this switch statement: + // - firstOperand, secondOperand, immediateValue, shiftAmount: For arithmetic, logical and move instructions + // - baseAddress, immediateOffset, shiftAmount: For LDR + // - numRegistersToLoad: For LDM and POP instructions + switch (decodedInstruction.instruction->code) + { + // Arithmetic operations that can change the PC + case ARM_INST_ADC: + case ARM_INST_ADCS: + case ARM_INST_ADD: + case ARM_INST_ADDS: + case ARM_INST_AND: + case ARM_INST_ANDS: + case ARM_INST_ASR: + case ARM_INST_ASRS: + case ARM_INST_BIC: + case ARM_INST_BICS: + case ARM_INST_EOR: + case ARM_INST_EORS: + case ARM_INST_ORR: + case ARM_INST_ORRS: + case ARM_INST_RSB: + case ARM_INST_RSBS: + case ARM_INST_RSC: + case ARM_INST_RSCS: + case ARM_INST_SBC: + case ARM_INST_SBCS: + case ARM_INST_SUB: + case ARM_INST_SUBS: + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_DATA_IMM: + if (decodedInstruction.numOperands != 3) + { + DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.__r[firstOperandIndex]; + + // Get immediateValue (at index=2) + immediateValue = decodedInstruction.op[2].value; + + break; + + case ARM_ADDR_DATA_REG: + if (decodedInstruction.numOperands != 3) + { + DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.__r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + break; + + case ARM_ADDR_DATA_SCALED_IMM: + if (decodedInstruction.numOperands != 4) + { + DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.__r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + // Get shiftAmount as immediate value (at index=3) + shiftAmount = decodedInstruction.op[3].value; + + break; + + + case ARM_ADDR_DATA_SCALED_REG: + if (decodedInstruction.numOperands != 4) + { + DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.__r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + // Get shiftAmount from register (at index=3) + shiftRegisterIndex = decodedInstruction.op[3].value; // second operand register index + shiftAmount = m_state.gpr.__r[shiftRegisterIndex]; + + break; + + case THUMB_ADDR_HR_HR: + if (decodedInstruction.numOperands != 2) + { + DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=0) + firstOperandIndex = decodedInstruction.op[0].value; // first operand register index + firstOperand = m_state.gpr.__r[firstOperandIndex]; + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + break; + + default: + break; + } + break; + + // Logical shifts and move operations that can change the PC + case ARM_INST_LSL: + case ARM_INST_LSLS: + case ARM_INST_LSR: + case ARM_INST_LSRS: + case ARM_INST_MOV: + case ARM_INST_MOVS: + case ARM_INST_MVN: + case ARM_INST_MVNS: + case ARM_INST_ROR: + case ARM_INST_RORS: + case ARM_INST_RRX: + case ARM_INST_RRXS: + // In these cases, the firstOperand is always 0, as if it does not exist + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_DATA_IMM: + if (decodedInstruction.numOperands != 2) + { + DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get immediateValue (at index=1) + immediateValue = decodedInstruction.op[1].value; + + break; + + case ARM_ADDR_DATA_REG: + if (decodedInstruction.numOperands != 2) + { + DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + break; + + case ARM_ADDR_DATA_SCALED_IMM: + if (decodedInstruction.numOperands != 3) + { + DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + // Get shiftAmount as immediate value (at index=2) + shiftAmount = decodedInstruction.op[2].value; + + break; + + + case ARM_ADDR_DATA_SCALED_REG: + if (decodedInstruction.numOperands != 3) + { + DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + // Get shiftAmount from register (at index=2) + shiftRegisterIndex = decodedInstruction.op[2].value; // second operand register index + shiftAmount = m_state.gpr.__r[shiftRegisterIndex]; + + break; + + case THUMB_ADDR_HR_HR: + if (decodedInstruction.numOperands != 2) + { + DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.__r[secondOperandIndex]; + + break; + + default: + break; + } + + break; + + // Simple branches, used to hop around within a routine + case ARM_INST_B: + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + break; + + // Branch-and-link, used to call ARM subroutines + case ARM_INST_BL: + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + break; + + // Branch-and-link with exchange, used to call opposite-mode subroutines + case ARM_INST_BLX: + if ((decodedInstruction.addressMode == ARM_ADDR_BRANCH_IMM) || + (decodedInstruction.addressMode == THUMB_ADDR_UNCOND)) + { + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + else // addressMode == ARM_ADDR_BRANCH_REG + { + // Unknown target unless we're branching to the PC itself, + // although this may not work properly with BLX + if (decodedInstruction.op[REG_RD].value == PC_REG) + { + // this should (almost) never happen + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + + // Get the branch address and return + if (decodedInstruction.numOperands != 1) + { + DNBLogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address in register (at index=0) + *targetPC = m_state.gpr.__r[decodedInstruction.op[0].value]; + return true; + } + break; + + // Branch with exchange, used to hop to opposite-mode code + // Branch to Jazelle code, used to execute Java; included here since it + // acts just like BX unless the Jazelle unit is active and JPC is + // already loaded into it. + case ARM_INST_BX: + case ARM_INST_BXJ: + // Unknown target unless we're branching to the PC itself, + // although this can never switch to Thumb mode and is + // therefore pretty much useless + if (decodedInstruction.op[REG_RD].value == PC_REG) + { + // this should (almost) never happen + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + + // Get the branch address and return + if (decodedInstruction.numOperands != 1) + { + DNBLogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address in register (at index=0) + *targetPC = m_state.gpr.__r[decodedInstruction.op[0].value]; + return true; + break; + + // Compare and branch on zero/non-zero (Thumb-16 only) + // Unusual condition check built into the instruction + case ARM_INST_CBZ: + case ARM_INST_CBNZ: + // Branch address is known at compile time + // Get the branch address and return + if (decodedInstruction.numOperands != 2) + { + DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address as an immediate value (at index=1) + *targetPC = decodedInstruction.op[1].value; + return true; + break; + + // Load register can be used to load PC, usually with a function pointer + case ARM_INST_LDR: + if (decodedInstruction.op[REG_RD].value != PC_REG) + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_LSWUB_IMM: + case ARM_ADDR_LSWUB_IMM_PRE: + case ARM_ADDR_LSWUB_IMM_POST: + if (decodedInstruction.numOperands != 3) + { + DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.__r[baseAddressIndex]; + + // Get immediateOffset (at index=2) + immediateOffset = decodedInstruction.op[2].value; + break; + + case ARM_ADDR_LSWUB_REG: + case ARM_ADDR_LSWUB_REG_PRE: + case ARM_ADDR_LSWUB_REG_POST: + if (decodedInstruction.numOperands != 3) + { + DNBLogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.__r[baseAddressIndex]; + + // Get immediateOffset from register (at index=2) + addressOffsetFromRegisterIndex = decodedInstruction.op[2].value; + addressOffsetFromRegister = m_state.gpr.__r[addressOffsetFromRegisterIndex]; + + break; + + case ARM_ADDR_LSWUB_SCALED: + case ARM_ADDR_LSWUB_SCALED_PRE: + case ARM_ADDR_LSWUB_SCALED_POST: + if (decodedInstruction.numOperands != 4) + { + DNBLogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.__r[baseAddressIndex]; + + // Get immediateOffset from register (at index=2) + addressOffsetFromRegisterIndex = decodedInstruction.op[2].value; + addressOffsetFromRegister = m_state.gpr.__r[addressOffsetFromRegisterIndex]; + + // Get shiftAmount (at index=3) + shiftAmount = decodedInstruction.op[3].value; + + break; + + default: + break; + } + break; + + // 32b load multiple operations can load the PC along with everything else, + // usually to return from a function call + case ARM_INST_LDMDA: + case ARM_INST_LDMDB: + case ARM_INST_LDMIA: + case ARM_INST_LDMIB: + if (decodedInstruction.op[LDM_REGLIST].value & PC_REGLIST_BIT) + { + if (decodedInstruction.numOperands != 2) + { + DNBLogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=0) + baseAddressIndex = decodedInstruction.op[0].value; + baseAddress = m_state.gpr.__r[baseAddressIndex]; + + // Get registerList from register (at index=1) + registerList16 = (uint16_t)decodedInstruction.op[1].value; + + // Count number of registers to load in the multiple register list excluding the PC + registerList16NoPC = registerList16&0x3FFF; // exclude the PC + numRegistersToLoad=0; + for (int i = 0; i < 16; i++) + { + if (registerList16NoPC & 0x1) numRegistersToLoad++; + registerList16NoPC = registerList16NoPC >> 1; + } + } + else + { + DNBLogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + break; + + // Normal 16-bit LD multiple can't touch R15, but POP can + case ARM_INST_POP: // Can also get the PC & updates SP + // Get baseAddress from SP (at index=0) + baseAddress = m_state.gpr.__sp; + + if (decodedInstruction.thumb16b) + { + // Get registerList from register (at index=0) + registerList8 = (uint8_t)decodedInstruction.op[0].value; + + // Count number of registers to load in the multiple register list + numRegistersToLoad=0; + for (int i = 0; i < 8; i++) + { + if (registerList8 & 0x1) numRegistersToLoad++; + registerList8 = registerList8 >> 1; + } + } + else + { + // Get registerList from register (at index=0) + registerList16 = (uint16_t)decodedInstruction.op[0].value; + + // Count number of registers to load in the multiple register list excluding the PC + registerList16NoPC = registerList16&0x3FFF; // exclude the PC + numRegistersToLoad=0; + for (int i = 0; i < 16; i++) + { + if (registerList16NoPC & 0x1) numRegistersToLoad++; + registerList16NoPC = registerList16NoPC >> 1; + } + } + break; + + // 16b TBB and TBH instructions load a jump address from a table + case ARM_INST_TBB: + case ARM_INST_TBH: + // Get baseAddress from register (at index=0) + baseAddressIndex = decodedInstruction.op[0].value; + baseAddress = m_state.gpr.__r[baseAddressIndex]; + + // Get immediateOffset from register (at index=1) + addressOffsetFromRegisterIndex = decodedInstruction.op[1].value; + addressOffsetFromRegister = m_state.gpr.__r[addressOffsetFromRegisterIndex]; + break; + + // ThumbEE branch-to-handler instructions: Jump to handlers at some offset + // from a special base pointer register (which is unknown at disassembly time) + case ARM_INST_HB: + case ARM_INST_HBP: +// TODO: ARM_INST_HB, ARM_INST_HBP + break; + + case ARM_INST_HBL: + case ARM_INST_HBLP: +// TODO: ARM_INST_HBL, ARM_INST_HBLP + break; + + // Breakpoint and software interrupt jump to interrupt handler (always ARM) + case ARM_INST_BKPT: + case ARM_INST_SMC: + case ARM_INST_SVC: + + // Return from exception, obviously modifies PC [interrupt only!] + case ARM_INST_RFEDA: + case ARM_INST_RFEDB: + case ARM_INST_RFEIA: + case ARM_INST_RFEIB: + + // Other instructions either can't change R15 or are "undefined" if you do, + // so no sane compiler should ever generate them & we don't care here. + // Also, R15 can only legally be used in a read-only manner for the + // various ARM addressing mode (to get PC-relative addressing of constants), + // but can NOT be used with any of the update modes. + default: + DNBLogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code); + return false; + break; + } + + // Adjust PC if PC is one of the input operands + if (baseAddressIndex == PC_REG) + { + if (currentPCIsThumb) + baseAddress += 4; + else + baseAddress += 8; + } + + if (firstOperandIndex == PC_REG) + { + if (currentPCIsThumb) + firstOperand += 4; + else + firstOperand += 8; + } + + if (secondOperandIndex == PC_REG) + { + if (currentPCIsThumb) + secondOperand += 4; + else + secondOperand += 8; + } + + if (addressOffsetFromRegisterIndex == PC_REG) + { + if (currentPCIsThumb) + addressOffsetFromRegister += 4; + else + addressOffsetFromRegister += 8; + } + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, + "%s: firstOperand=%8.8x, secondOperand=%8.8x, immediateValue = %d, shiftAmount = %d, baseAddress = %8.8x, addressOffsetFromRegister = %8.8x, immediateOffset = %d, numRegistersToLoad = %d", + __FUNCTION__, + firstOperand, + secondOperand, + immediateValue, + shiftAmount, + baseAddress, + addressOffsetFromRegister, + immediateOffset, + numRegistersToLoad); + + + // Calculate following values after applying shiftAmount: + // - immediateOffsetAfterShift, secondOperandAfterShift + + switch (decodedInstruction.scaleMode) + { + case ARM_SCALE_NONE: + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister; + secondOperandAfterShift = secondOperand; + break; + + case ARM_SCALE_LSL: // Logical shift left + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister << shiftAmount; + secondOperandAfterShift = secondOperand << shiftAmount; + break; + + case ARM_SCALE_LSR: // Logical shift right + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister >> shiftAmount; + secondOperandAfterShift = secondOperand >> shiftAmount; + break; + + case ARM_SCALE_ASR: // Arithmetic shift right + asm("mov %0, %1, asr %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount)); + asm("mov %0, %1, asr %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount)); + break; + + case ARM_SCALE_ROR: // Rotate right + asm("mov %0, %1, ror %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount)); + asm("mov %0, %1, ror %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount)); + break; + + case ARM_SCALE_RRX: // Rotate right, pulling in carry (1-bit shift only) + asm("mov %0, %1, rrx" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister)); + asm("mov %0, %1, rrx" : "=r" (secondOperandAfterShift) : "r" (secondOperand)); + break; + } + + // Emulate instruction to calculate targetPC + // All branches are already handled in the first switch statement. A branch should not reach this switch + switch (decodedInstruction.instruction->code) + { + // Arithmetic operations that can change the PC + case ARM_INST_ADC: + case ARM_INST_ADCS: + // Add with Carry + *targetPC = firstOperand + (secondOperandAfterShift + immediateValue) + cpsr_c; + break; + + case ARM_INST_ADD: + case ARM_INST_ADDS: + *targetPC = firstOperand + (secondOperandAfterShift + immediateValue); + break; + + case ARM_INST_AND: + case ARM_INST_ANDS: + *targetPC = firstOperand & (secondOperandAfterShift + immediateValue); + break; + + case ARM_INST_ASR: + case ARM_INST_ASRS: + asm("mov %0, %1, asr %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_BIC: + case ARM_INST_BICS: + asm("bic %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_EOR: + case ARM_INST_EORS: + asm("eor %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_ORR: + case ARM_INST_ORRS: + asm("orr %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_RSB: + case ARM_INST_RSBS: + asm("rsb %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_RSC: + case ARM_INST_RSCS: + myTargetPC = secondOperandAfterShift - (firstOperand + !cpsr_c); + *targetPC = myTargetPC; + break; + + case ARM_INST_SBC: + case ARM_INST_SBCS: + asm("sbc %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue + !cpsr_c)); + *targetPC = myTargetPC; + break; + + case ARM_INST_SUB: + case ARM_INST_SUBS: + asm("sub %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + // Logical shifts and move operations that can change the PC + case ARM_INST_LSL: + case ARM_INST_LSLS: + case ARM_INST_LSR: + case ARM_INST_LSRS: + case ARM_INST_MOV: + case ARM_INST_MOVS: + case ARM_INST_ROR: + case ARM_INST_RORS: + case ARM_INST_RRX: + case ARM_INST_RRXS: + myTargetPC = secondOperandAfterShift + immediateValue; + *targetPC = myTargetPC; + break; + + case ARM_INST_MVN: + case ARM_INST_MVNS: + myTargetPC = !(secondOperandAfterShift + immediateValue); + *targetPC = myTargetPC; + break; + + // Load register can be used to load PC, usually with a function pointer + case ARM_INST_LDR: + switch (decodedInstruction.addressMode) { + case ARM_ADDR_LSWUB_IMM_POST: + case ARM_ADDR_LSWUB_REG_POST: + case ARM_ADDR_LSWUB_SCALED_POST: + addressWherePCLives = baseAddress; + break; + + case ARM_ADDR_LSWUB_IMM: + case ARM_ADDR_LSWUB_REG: + case ARM_ADDR_LSWUB_SCALED: + case ARM_ADDR_LSWUB_IMM_PRE: + case ARM_ADDR_LSWUB_REG_PRE: + case ARM_ADDR_LSWUB_SCALED_PRE: + addressWherePCLives = baseAddress + (addressOffsetFromRegisterAfterShift + immediateOffset); + break; + + default: + break; + } + + mypid = m_thread->ProcessID(); + if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t)) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + // 32b load multiple operations can load the PC along with everything else, + // usually to return from a function call + case ARM_INST_LDMDA: + mypid = m_thread->ProcessID(); + addressWherePCLives = baseAddress; + if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t)) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMDB: + mypid = m_thread->ProcessID(); + addressWherePCLives = baseAddress - 4; + if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t)) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMIB: + mypid = m_thread->ProcessID(); + addressWherePCLives = baseAddress + numRegistersToLoad*4 + 4; + if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t)) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMIA: // same as pop + // Normal 16-bit LD multiple can't touch R15, but POP can + case ARM_INST_POP: // Can also get the PC & updates SP + mypid = m_thread->ProcessID(); + addressWherePCLives = baseAddress + numRegistersToLoad*4; + if (DNBProcessMemoryRead(mypid, addressWherePCLives, sizeof(nub_addr_t), &myTargetPC) != sizeof(nub_addr_t)) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + // 16b TBB and TBH instructions load a jump address from a table + case ARM_INST_TBB: + mypid = m_thread->ProcessID(); + addressWherePCLives = baseAddress + addressOffsetFromRegisterAfterShift; + if (DNBProcessMemoryRead(mypid, addressWherePCLives, 1, &halfwords) != 1) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the TBB instruction!", addressWherePCLives); + return false; + } + // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords + *targetPC = (currentPC + 4) + 2*halfwords; + break; + + case ARM_INST_TBH: + mypid = m_thread->ProcessID(); + addressWherePCLives = ((baseAddress + (addressOffsetFromRegisterAfterShift << 1)) & ~0x1); + if (DNBProcessMemoryRead(mypid, addressWherePCLives, 2, &halfwords) != 2) + { + DNBLogError("Could not read memory at %8.8x to get targetPC when processing the TBH instruction!", addressWherePCLives); + return false; + } + // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords + *targetPC = (currentPC + 4) + 2*halfwords; + break; + + // ThumbEE branch-to-handler instructions: Jump to handlers at some offset + // from a special base pointer register (which is unknown at disassembly time) + case ARM_INST_HB: + case ARM_INST_HBP: + // TODO: ARM_INST_HB, ARM_INST_HBP + break; + + case ARM_INST_HBL: + case ARM_INST_HBLP: + // TODO: ARM_INST_HBL, ARM_INST_HBLP + break; + + // Breakpoint and software interrupt jump to interrupt handler (always ARM) + case ARM_INST_BKPT: + case ARM_INST_SMC: + case ARM_INST_SVC: + // TODO: ARM_INST_BKPT, ARM_INST_SMC, ARM_INST_SVC + break; + + // Return from exception, obviously modifies PC [interrupt only!] + case ARM_INST_RFEDA: + case ARM_INST_RFEDB: + case ARM_INST_RFEIA: + case ARM_INST_RFEIB: + // TODO: ARM_INST_RFEDA, ARM_INST_RFEDB, ARM_INST_RFEIA, ARM_INST_RFEIB + break; + + // Other instructions either can't change R15 or are "undefined" if you do, + // so no sane compiler should ever generate them & we don't care here. + // Also, R15 can only legally be used in a read-only manner for the + // various ARM addressing mode (to get PC-relative addressing of constants), + // but can NOT be used with any of the update modes. + default: + DNBLogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code); + return false; + break; + } + + return true; +} + +void +DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, nub_addr_t *nextPC, bool *nextPCIsThumb) +{ + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup() called"); + + nub_addr_t targetPC = INVALID_NUB_ADDRESS; + uint32_t registerValue; + arm_error_t decodeError; + nub_addr_t currentPCInITBlock, nextPCInITBlock; + int i; + bool last_decoded_instruction_executes = true; + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: default nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM"); + + // Update *nextPC and *nextPCIsThumb for special cases + if (m_last_decode_thumb.itBlockRemaining) // we are in an IT block + { + // Set the nextPC to the PC of the instruction which will execute in the IT block + // If none of the instruction execute in the IT block based on the condition flags, + // then point to the instruction immediately following the IT block + const int itBlockRemaining = m_last_decode_thumb.itBlockRemaining; + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: itBlockRemaining=%8.8x", __FUNCTION__, itBlockRemaining); + + // Determine the PC at which the next instruction resides + if (m_last_decode_arm.thumb16b) + currentPCInITBlock = currentPC + 2; + else + currentPCInITBlock = currentPC + 4; + + for (i = 0; i < itBlockRemaining; i++) + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: currentPCInITBlock=%8.8x", __FUNCTION__, currentPCInITBlock); + decodeError = DecodeInstructionUsingDisassembler(currentPCInITBlock, cpsr, &m_last_decode_arm, &m_last_decode_thumb, &nextPCInITBlock); + + if (decodeError != ARM_SUCCESS) + DNBLogError("unable to disassemble instruction at 0x%8.8lx", currentPCInITBlock); + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: condition=%d", __FUNCTION__, m_last_decode_arm.condition); + if (ConditionPassed(m_last_decode_arm.condition, cpsr)) + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition codes matched for instruction %d", __FUNCTION__, i); + break; // break from the for loop + } + else + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition codes DID NOT matched for instruction %d", __FUNCTION__, i); + } + + // update currentPC and nextPCInITBlock + currentPCInITBlock = nextPCInITBlock; + } + + if (i == itBlockRemaining) // We came out of the IT block without executing any instructions + last_decoded_instruction_executes = false; + + *nextPC = currentPCInITBlock; + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: After IT block step-through: *nextPC=%8.8x", __FUNCTION__, *nextPC); + } + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, + "%s: cpsr = %8.8x, thumb16b = %d, thumb = %d, branch = %d, conditional = %d, knownTarget = %d, links = %d, canSwitchMode = %d, doesSwitchMode = %d", + __FUNCTION__, + cpsr, + m_last_decode_arm.thumb16b, + m_last_decode_arm.thumb, + m_last_decode_arm.branch, + m_last_decode_arm.conditional, + m_last_decode_arm.knownTarget, + m_last_decode_arm.links, + m_last_decode_arm.canSwitchMode, + m_last_decode_arm.doesSwitchMode); + + + if (last_decoded_instruction_executes && // Was this a conditional instruction that did execute? + m_last_decode_arm.branch && // Can this instruction change the PC? + (m_last_decode_arm.instruction->code != ARM_INST_SVC)) // If this instruction is not an SVC instruction + { + // Set targetPC. Compute if needed. + if (m_last_decode_arm.knownTarget) + { + // Fixed, known PC-relative + targetPC = m_last_decode_arm.targetPC; + } + else + { + // if targetPC is not known at compile time (PC-relative target), compute targetPC + if (!ComputeNextPC(currentPC, m_last_decode_arm, currentPCIsThumb, &targetPC)) + { + DNBLogError("%s: Unable to compute targetPC for instruction at 0x%8.8lx", __FUNCTION__, currentPC); + targetPC = INVALID_NUB_ADDRESS; + } + } + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: targetPC=0x%8.8x, cpsr=0x%8.8x, condition=0x%hhx", __FUNCTION__, targetPC, cpsr, m_last_decode_arm.condition); + + // Refine nextPC computation + if ((m_last_decode_arm.instruction->code == ARM_INST_CBZ) || + (m_last_decode_arm.instruction->code == ARM_INST_CBNZ)) + { + // Compare and branch on zero/non-zero (Thumb-16 only) + // Unusual condition check built into the instruction + registerValue = m_state.gpr.__r[m_last_decode_arm.op[REG_RD].value]; + + if (m_last_decode_arm.instruction->code == ARM_INST_CBZ) + { + if (registerValue == 0) + *nextPC = targetPC; + } + else + { + if (registerValue != 0) + *nextPC = targetPC; + } + } + else if (m_last_decode_arm.conditional) // Is the change conditional on flag results? + { + if (ConditionPassed(m_last_decode_arm.condition, cpsr)) // conditions match + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition matched!", __FUNCTION__); + *nextPC = targetPC; + } + else + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Condition did not match!", __FUNCTION__); + } + } + else + { + *nextPC = targetPC; + } + + // Refine nextPCIsThumb computation + if (m_last_decode_arm.doesSwitchMode) + { + *nextPCIsThumb = !currentPCIsThumb; + } + else if (m_last_decode_arm.canSwitchMode) + { + // Legal to switch ARM <--> Thumb mode with this branch + // dependent on bit[0] of targetPC + *nextPCIsThumb = (*nextPC & 1u) != 0; + } + else + { + *nextPCIsThumb = currentPCIsThumb; + } + } + + DNBLogThreadedIf(LOG_STEP, "%s: calculated nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM"); +} + + +arm_error_t +DNBArchMachARM::DecodeInstructionUsingDisassembler(nub_addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, nub_addr_t *next_pc) +{ + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: pc=0x%8.8x, cpsr=0x%8.8x", __FUNCTION__, curr_pc, curr_cpsr); + + const uint32_t isetstate_mask = MASK_CPSR_T | MASK_CPSR_J; + const uint32_t curr_isetstate = curr_cpsr & isetstate_mask; + uint32_t opcode32; + nub_addr_t nextPC = curr_pc; + arm_error_t decodeReturnCode = ARM_SUCCESS; + + m_last_decode_pc = curr_pc; + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc); + + switch (curr_isetstate) { + case 0x0: // ARM Instruction + // Read the ARM opcode + if (m_thread->Process()->Task().ReadMemory(curr_pc, 4, &opcode32) != 4) + { + DNBLogError("unable to read opcode bits 31:0 for an ARM opcode at 0x%8.8lx", curr_pc); + decodeReturnCode = ARM_ERROR; + } + else + { + nextPC += 4; + decodeReturnCode = ArmDisassembler((uint64_t)curr_pc, opcode32, false, decodedInstruction, NULL, 0, NULL, 0); + + if (decodeReturnCode != ARM_SUCCESS) + DNBLogError("Unable to decode ARM instruction 0x%8.8x at 0x%8.8lx", opcode32, curr_pc); + } + break; + + case 0x20: // Thumb Instruction + uint16_t opcode16; + // Read the a 16 bit Thumb opcode + if (m_thread->Process()->Task().ReadMemory(curr_pc, 2, &opcode16) != 2) + { + DNBLogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc); + decodeReturnCode = ARM_ERROR; + } + else + { + nextPC += 2; + opcode32 = opcode16; + + decodeReturnCode = ThumbDisassembler((uint64_t)curr_pc, opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0); + + switch (decodeReturnCode) { + case ARM_SKIP: + // 32 bit thumb opcode + nextPC += 2; + if (m_thread->Process()->Task().ReadMemory(curr_pc+2, 2, &opcode16) != 2) + { + DNBLogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc+2); + } + else + { + opcode32 = (opcode32 << 16) | opcode16; + + decodeReturnCode = ThumbDisassembler((uint64_t)(curr_pc+2), opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0); + + if (decodeReturnCode != ARM_SUCCESS) + DNBLogError("Unable to decode 2nd half of Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc+2); + break; + } + break; + + case ARM_SUCCESS: + // 16 bit thumb opcode; at this point we are done decoding the opcode + break; + + default: + DNBLogError("Unable to decode Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc); + decodeReturnCode = ARM_ERROR; + break; + } + } + break; + + default: + break; + } + + if (next_pc) + *next_pc = nextPC; + + return decodeReturnCode; +} + +nub_bool_t +DNBArchMachARM::BreakpointHit(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton) +{ + nub_addr_t bkpt_pc = (nub_addr_t)baton; + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s(pid = %i, tid = %4.4x, breakID = %u, baton = %p): Setting PC to 0x%8.8x", __FUNCTION__, pid, tid, breakID, baton, bkpt_pc); + return DNBThreadSetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_PC, bkpt_pc); +} + +// Set the single step bit in the processor status register. +kern_return_t +DNBArchMachARM::SetSingleStepSoftwareBreakpoints() +{ + DNBError err; + err = GetGPRState(false); + + if (err.Fail()) + { + err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__); + return err.Error(); + } + + nub_addr_t curr_pc = m_state.gpr.__pc; + uint32_t curr_cpsr = m_state.gpr.__cpsr; + nub_addr_t next_pc = curr_pc; + + bool curr_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0; + bool next_pc_is_thumb = curr_pc_is_thumb; + + uint32_t curr_itstate = ((curr_cpsr & 0x6000000) >> 25) | ((curr_cpsr & 0xFC00) >> 8); + bool inITBlock = (curr_itstate & 0xF) ? 1 : 0; + bool lastInITBlock = ((curr_itstate & 0xF) == 0x8) ? 1 : 0; + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: curr_pc=0x%8.8x (%s), curr_itstate=0x%x, inITBlock=%d, lastInITBlock=%d", __FUNCTION__, curr_pc, curr_pc_is_thumb ? "Thumb" : "ARM", curr_itstate, inITBlock, lastInITBlock); + + // If the instruction is not in the IT block, then decode using the Disassembler and compute next_pc + if (!inITBlock) + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Decoding an instruction NOT in the IT block", __FUNCTION__); + + arm_error_t decodeReturnCode = DecodeInstructionUsingDisassembler(curr_pc, curr_cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc); + + if (decodeReturnCode != ARM_SUCCESS) + { + err = KERN_INVALID_ARGUMENT; + DNBLogError("DNBArchMachARM::SetSingleStepSoftwareBreakpoints: Unable to disassemble instruction at 0x%8.8lx", curr_pc); + } + } + else + { + next_pc = curr_pc + ((m_last_decode_arm.thumb16b) ? 2 : 4); + } + + // Instruction is NOT in the IT block OR + if (!inITBlock) + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: normal instruction", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else if (inITBlock && !m_last_decode_arm.setsFlags) + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: IT instruction that doesn't set flags", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else if (lastInITBlock && m_last_decode_arm.branch) + { + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: IT instruction which last in the IT block and is a branch", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else + { + // Instruction is in IT block and can modify the CPSR flags + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: IT instruction that sets flags", __FUNCTION__); + + // NOTE: When this point of code is reached, the instruction at curr_pc has already been decoded + // inside the function ThreadDidStop(). Therefore m_last_decode_arm, m_last_decode_thumb + // reflect the decoded instruction at curr_pc + + // If we find an instruction inside the IT block which will set/modify the condition flags (NZCV bits in CPSR), + // we set breakpoints at all remaining instructions inside the IT block starting from the instruction immediately + // following this one AND a breakpoint at the instruction immediately following the IT block. We do this because + // we cannot determine the next_pc until the instruction at which we are currently stopped executes. Hence we + // insert (m_last_decode_thumb.itBlockRemaining+1) 16-bit Thumb breakpoints at consecutive memory locations + // starting at addrOfNextInstructionInITBlock. We record these breakpoints in class variable m_sw_single_step_itblock_break_id[], + // and also record the total number of IT breakpoints set in the variable 'm_sw_single_step_itblock_break_count'. + + // The instructions inside the IT block, which are replaced by the 16-bit Thumb breakpoints (opcode=0xDEFE) + // instructions, can be either Thumb-16 or Thumb-32. When a Thumb-32 instruction (say, inst#1) is replaced Thumb + // by a 16-bit breakpoint (OS only supports 16-bit breakpoints in Thumb mode and 32-bit breakpoints in ARM mode), the + // breakpoint for the next instruction (say instr#2) is saved in the upper half of this Thumb-32 (instr#1) + // instruction. Hence if the execution stops at Breakpoint2 corresponding to instr#2, the PC is offset by 16-bits. + // We therefore have to keep track of PC of each instruction in the IT block that is being replaced with the 16-bit + // Thumb breakpoint, to ensure that when the breakpoint is hit, the PC is adjusted to the correct value. We save + // the actual PC corresponding to each instruction in the IT block by associating a call back with each breakpoint + // we set and passing it as a baton. When the breakpoint hits and the callback routine is called, the routine + // adjusts the PC based on the baton that is passed to it. + + nub_addr_t addrOfNextInstructionInITBlock, pcInITBlock, nextPCInITBlock, bpAddressInITBlock; + uint16_t opcode16; + uint32_t opcode32; + + addrOfNextInstructionInITBlock = (m_last_decode_arm.thumb16b) ? curr_pc + 2 : curr_pc + 4; + + pcInITBlock = addrOfNextInstructionInITBlock; + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: itBlockRemaining=%d", __FUNCTION__, m_last_decode_thumb.itBlockRemaining); + + m_sw_single_step_itblock_break_count = 0; + for (int i = 0; i <= m_last_decode_thumb.itBlockRemaining; i++) + { + if (NUB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + { + DNBLogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Array m_sw_single_step_itblock_break_id should not contain any valid breakpoint IDs at this point. But found a valid breakID=%d at index=%d", m_sw_single_step_itblock_break_id[i], i); + } + else + { + nextPCInITBlock = pcInITBlock; + // Compute nextPCInITBlock based on opcode present at pcInITBlock + if (m_thread->Process()->Task().ReadMemory(pcInITBlock, 2, &opcode16) == 2) + { + opcode32 = opcode16; + nextPCInITBlock += 2; + + // Check for 32 bit thumb opcode and read the upper 16 bits if needed + if (((opcode32 & 0xE000) == 0xE000) && (opcode32 & 0x1800)) + { + // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for + // a 32 bit Thumb opcode + // Read bits 31:16 of a 32 bit Thumb opcode + if (m_thread->Process()->Task().ReadMemory(pcInITBlock+2, 2, &opcode16) == 2) + { + // 32 bit thumb opcode + opcode32 = (opcode32 << 16) | opcode16; + nextPCInITBlock += 2; + } + else + { + DNBLogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", nextPCInITBlock); + } + } + } + else + { + DNBLogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Error reading 16-bit Thumb instruction at pc=0x%8.8x", nextPCInITBlock); + } + + + // Set breakpoint and associate a callback function with it + bpAddressInITBlock = addrOfNextInstructionInITBlock + 2*i; + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Setting IT breakpoint[%d] at address: 0x%8.8x", __FUNCTION__, i, bpAddressInITBlock); + + m_sw_single_step_itblock_break_id[i] = m_thread->Process()->CreateBreakpoint(bpAddressInITBlock, 2, false, m_thread->ThreadID()); + if (!NUB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + err = KERN_INVALID_ARGUMENT; + else + { + DNBLogThreadedIf(LOG_STEP, "%s: Set IT breakpoint[%i]=%d set at 0x%8.8x for instruction at 0x%8.8x", __FUNCTION__, i, m_sw_single_step_itblock_break_id[i], bpAddressInITBlock, pcInITBlock); + + // Set the breakpoint callback for these special IT breakpoints + // so that if one of these breakpoints gets hit, it knows to + // update the PC to the original address of the conditional + // IT instruction. + DNBBreakpointSetCallback(m_thread->ProcessID(), m_sw_single_step_itblock_break_id[i], DNBArchMachARM::BreakpointHit, (void*)pcInITBlock); + m_sw_single_step_itblock_break_count++; + } + } + + pcInITBlock = nextPCInITBlock; + } + + DNBLogThreadedIf(LOG_STEP | LOG_VERBOSE, "%s: Set %u IT software single breakpoints.", __FUNCTION__, m_sw_single_step_itblock_break_count); + + } + + DNBLogThreadedIf(LOG_STEP, "%s: next_pc=0x%8.8x (%s)", __FUNCTION__, next_pc, next_pc_is_thumb ? "Thumb" : "ARM"); + + if (next_pc & 0x1) + { + assert(next_pc_is_thumb); + } + + if (next_pc_is_thumb) + { + next_pc &= ~0x1; + } + else + { + assert((next_pc & 0x3) == 0); + } + + if (!inITBlock || (inITBlock && !m_last_decode_arm.setsFlags) || (lastInITBlock && m_last_decode_arm.branch)) + { + err = KERN_SUCCESS; + +#if defined DNB_ARCH_MACH_ARM_DEBUG_SW_STEP + m_sw_single_step_next_pc = next_pc; + if (next_pc_is_thumb) + m_sw_single_step_next_pc |= 1; // Set bit zero if the next PC is expected to be Thumb +#else + const DNBBreakpoint *bp = m_thread->Process()->Breakpoints().FindByAddress(next_pc); + + if (bp == NULL) + { + m_sw_single_step_break_id = m_thread->Process()->CreateBreakpoint(next_pc, next_pc_is_thumb ? 2 : 4, false, m_thread->ThreadID()); + if (!NUB_BREAK_ID_IS_VALID(m_sw_single_step_break_id)) + err = KERN_INVALID_ARGUMENT; + DNBLogThreadedIf(LOG_STEP, "%s: software single step breakpoint with breakID=%d set at 0x%8.8x", __FUNCTION__, m_sw_single_step_break_id, next_pc); + } +#endif + } + + return err.Error(); +} + +uint32_t +DNBArchMachARM::NumSupportedHardwareBreakpoints() +{ + // Set the init value to something that will let us know that we need to + // autodetect how many breakpoints are supported dynamically... + static uint32_t g_num_supported_hw_breakpoints = UINT_MAX; + if (g_num_supported_hw_breakpoints == UINT_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_breakpoints = 0; + + // Read the DBGDIDR to get the number of available hardware breakpoints + // However, in some of our current armv7 processors, hardware + // breakpoints/watchpoints were not properly connected. So detect those + // cases using a field in a sysctl. For now we are using "hw.cpusubtype" + // field to distinguish CPU architectures. This is a hack until we can + // get fixed, at which point we will switch to + // using a different sysctl string that will tell us how many BRPs + // are available to us directly without having to read DBGDIDR. + uint32_t register_DBGDIDR; + + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numBRPs = bits(register_DBGDIDR, 27, 24); + // Zero is reserved for the BRP count, so don't increment it if it is zero + if (numBRPs > 0) + numBRPs++; + DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, numBRPs); + + if (numBRPs > 0) + { + uint32_t cpusubtype; + size_t len; + len = sizeof(cpusubtype); + // TODO: remove this hack and change to using hw.optional.xx when implmented + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype); + if (cpusubtype == CPU_SUBTYPE_ARM_V7) + DNBLogThreadedIf(LOG_THREAD, "Hardware breakpoints disabled for armv7 (rdar://problem/6372672)"); + else + g_num_supported_hw_breakpoints = numBRPs; + } + } + + } + return g_num_supported_hw_breakpoints; +} + + +uint32_t +DNBArchMachARM::NumSupportedHardwareWatchpoints() +{ + // Set the init value to something that will let us know that we need to + // autodetect how many watchpoints are supported dynamically... + static uint32_t g_num_supported_hw_watchpoints = UINT_MAX; + if (g_num_supported_hw_watchpoints == UINT_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_watchpoints = 0; + // Read the DBGDIDR to get the number of available hardware breakpoints + // However, in some of our current armv7 processors, hardware + // breakpoints/watchpoints were not properly connected. So detect those + // cases using a field in a sysctl. For now we are using "hw.cpusubtype" + // field to distinguish CPU architectures. This is a hack until we can + // get fixed, at which point we will switch to + // using a different sysctl string that will tell us how many WRPs + // are available to us directly without having to read DBGDIDR. + + uint32_t register_DBGDIDR; + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1; + DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, numWRPs); + + if (numWRPs > 0) + { + uint32_t cpusubtype; + size_t len; + len = sizeof(cpusubtype); + // TODO: remove this hack and change to using hw.optional.xx when implmented + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype); + + if (cpusubtype == CPU_SUBTYPE_ARM_V7) + DNBLogThreadedIf(LOG_THREAD, "Hardware watchpoints disabled for armv7 (rdar://problem/6372672)"); + else + g_num_supported_hw_watchpoints = numWRPs; + } + } + + } + return g_num_supported_hw_watchpoints; +} + + +uint32_t +DNBArchMachARM::EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size) +{ + // Make sure our address isn't bogus + if (addr & 1) + return INVALID_NUB_HW_INDEX; + + kern_return_t kret = GetDBGState(false); + + if (kret == KERN_SUCCESS) + { + const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints(); + uint32_t i; + for (i=0; i 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { + DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size); + } + } + + return INVALID_NUB_HW_INDEX; +} + +bool +DNBArchMachARM::DisableHardwareBreakpoint (uint32_t hw_index) +{ + kern_return_t kret = GetDBGState(false); + + const uint32_t num_hw_points = NumSupportedHardwareBreakpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + m_state.dbg.__bcr[hw_index] = 0; + DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x", + hw_index, + hw_index, + m_state.dbg.__bvr[hw_index], + hw_index, + m_state.dbg.__bcr[hw_index]); + + kret = SetDBGState(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +uint32_t +DNBArchMachARM::EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write) +{ + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(addr = %8.8p, size = %u, read = %u, write = %u)", addr, size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can't watch zero bytes + if (size == 0) + return INVALID_NUB_HW_INDEX; + + // We must watch for either read or write + if (read == false && write == false) + return INVALID_NUB_HW_INDEX; + + // Can't watch more than 4 bytes per WVR/WCR pair + if (size > 4) + return INVALID_NUB_HW_INDEX; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair. Since we have at most so we can only watch + // until the next 4 byte boundary and we need to make sure we can properly + // encode this. + uint32_t addr_word_offset = addr % 4; + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset); + + uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset; + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask); + if (byte_mask > 0xfu) + return INVALID_NUB_HW_INDEX; + + // Read the debug state + kern_return_t kret = GetDBGState(false); + + if (kret == KERN_SUCCESS) + { + // Check to make sure we have the needed hardware support + uint32_t i = 0; + + for (i=0; i 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return INVALID_NUB_HW_INDEX; +} + +bool +DNBArchMachARM::DisableHardwareWatchpoint (uint32_t hw_index) +{ + kern_return_t kret = GetDBGState(false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + m_state.dbg.__wcr[hw_index] = 0; + DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x", + hw_index, + hw_index, + m_state.dbg.__wvr[hw_index], + hw_index, + m_state.dbg.__wcr[hw_index]); + + kret = SetDBGState(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit ARMV6. +//---------------------------------------------------------------------- +enum gpr_regnums +{ + e_regNumGPR_r0 = 0, + e_regNumGPR_r1, + e_regNumGPR_r2, + e_regNumGPR_r3, + e_regNumGPR_r4, + e_regNumGPR_r5, + e_regNumGPR_r6, + e_regNumGPR_r7, + e_regNumGPR_r8, + e_regNumGPR_r9, + e_regNumGPR_r10, + e_regNumGPR_r11, + e_regNumGPR_r12, + e_regNumGPR_sp, + e_regNumGPR_lr, + e_regNumGPR_pc, + e_regNumGPR_cpsr +}; + +// General purpose registers +static DNBRegisterInfo g_gpr_registers[] = +{ + { "r0" , Uint, 4, Hex }, + { "r1" , Uint, 4, Hex }, + { "r2" , Uint, 4, Hex }, + { "r3" , Uint, 4, Hex }, + { "r4" , Uint, 4, Hex }, + { "r5" , Uint, 4, Hex }, + { "r6" , Uint, 4, Hex }, + { "r7" , Uint, 4, Hex }, + { "r8" , Uint, 4, Hex }, + { "r9" , Uint, 4, Hex }, + { "r10" , Uint, 4, Hex }, + { "r11" , Uint, 4, Hex }, + { "r12" , Uint, 4, Hex }, + { "sp" , Uint, 4, Hex }, + { "lr" , Uint, 4, Hex }, + { "pc" , Uint, 4, Hex }, + { "cpsr" , Uint, 4, Hex }, +}; + +// Floating point registers +static DNBRegisterInfo g_vfp_registers[] = +{ + { "s0" , IEEE754, 4, Float }, + { "s1" , IEEE754, 4, Float }, + { "s2" , IEEE754, 4, Float }, + { "s3" , IEEE754, 4, Float }, + { "s4" , IEEE754, 4, Float }, + { "s5" , IEEE754, 4, Float }, + { "s6" , IEEE754, 4, Float }, + { "s7" , IEEE754, 4, Float }, + { "s8" , IEEE754, 4, Float }, + { "s9" , IEEE754, 4, Float }, + { "s10" , IEEE754, 4, Float }, + { "s11" , IEEE754, 4, Float }, + { "s12" , IEEE754, 4, Float }, + { "s13" , IEEE754, 4, Float }, + { "s14" , IEEE754, 4, Float }, + { "s15" , IEEE754, 4, Float }, + { "s16" , IEEE754, 4, Float }, + { "s17" , IEEE754, 4, Float }, + { "s18" , IEEE754, 4, Float }, + { "s19" , IEEE754, 4, Float }, + { "s20" , IEEE754, 4, Float }, + { "s21" , IEEE754, 4, Float }, + { "s22" , IEEE754, 4, Float }, + { "s23" , IEEE754, 4, Float }, + { "s24" , IEEE754, 4, Float }, + { "s25" , IEEE754, 4, Float }, + { "s26" , IEEE754, 4, Float }, + { "s27" , IEEE754, 4, Float }, + { "s28" , IEEE754, 4, Float }, + { "s29" , IEEE754, 4, Float }, + { "s30" , IEEE754, 4, Float }, + { "s31" , IEEE754, 4, Float }, + { "fpscr" , Uint, 4, Hex } +}; + +// Exception registers + +static DNBRegisterInfo g_exc_registers[] = +{ + { "dar" , Uint, 4, Hex }, + { "dsisr" , Uint, 4, Hex }, + { "exception" , Uint, 4, Hex } +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo); +const size_t k_num_vfp_registers = sizeof(g_vfp_registers)/sizeof(DNBRegisterInfo); +const size_t k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +// Total number of registers for this architecture +const size_t k_num_armv6_registers = k_num_gpr_registers + k_num_vfp_registers + k_num_exc_registers; + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const DNBRegisterSetInfo g_reg_sets[] = +{ + { "ARMV6 Registers", NULL, k_num_armv6_registers }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_vfp_registers, k_num_vfp_registers }, + { "Exception State Registers", g_exc_registers, k_num_exc_registers } +}; +// Total number of register sets for this architecture +const size_t k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo); + + +const DNBRegisterSetInfo * +DNBArchMachARM::GetRegisterSetInfo(nub_size_t *num_reg_sets) const +{ + *num_reg_sets = k_num_register_sets; + return g_reg_sets; +} + +bool +DNBArchMachARM::GetRegisterValue(int set, int reg, DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = e_regNumGPR_pc; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = e_regNumGPR_sp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = e_regNumGPR_r7; // is this the right reg? + break; + + case GENERIC_REGNUM_RA: // Return Address + set = e_regSetGPR; + reg = e_regNumGPR_lr; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = e_regNumGPR_cpsr; + break; + + default: + return false; + } + } + + if (GetRegisterState(set, false) != KERN_SUCCESS) + return false; + + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + value->info = *regInfo; + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + value->value.uint32 = m_state.gpr.__r[reg]; + return true; + } + break; + + case e_regSetVFP: + if (reg < 32) + { + value->value.uint32 = m_state.vfp.__r[reg]; + return true; + } + else if (reg == 32) + { + value->value.uint32 = m_state.vfp.__fpscr; + return true; + } + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + value->value.uint32 = (&m_state.exc.__exception)[reg]; + return true; + } + break; + } + } + return false; +} + +bool +DNBArchMachARM::SetRegisterValue(int set, int reg, const DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = e_regNumGPR_pc; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = e_regNumGPR_sp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = e_regNumGPR_r7; + break; + + case GENERIC_REGNUM_RA: // Return Address + set = e_regSetGPR; + reg = e_regNumGPR_lr; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = e_regNumGPR_cpsr; + break; + + default: + return false; + } + } + + if (GetRegisterState(set, false) != KERN_SUCCESS) + return false; + + bool success = false; + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + m_state.gpr.__r[reg] = value->value.uint32; + success = true; + } + break; + + case e_regSetVFP: + if (reg < 32) + { + m_state.vfp.__r[reg] = value->value.float64; + success = true; + } + else if (reg == 32) + { + m_state.vfp.__fpscr = value->value.uint32; + success = true; + } + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + (&m_state.exc.__exception)[reg] = value->value.uint32; + success = true; + } + break; + } + + } + if (success) + return SetRegisterState(set) == KERN_SUCCESS; + return false; +} + +kern_return_t +DNBArchMachARM::GetRegisterState(int set, bool force) +{ + switch (set) + { + case e_regSetALL: return GetGPRState(force) | + GetVFPState(force) | + GetEXCState(force) | + GetDBGState(force); + case e_regSetGPR: return GetGPRState(force); + case e_regSetVFP: return GetVFPState(force); + case e_regSetEXC: return GetEXCState(force); + case e_regSetDBG: return GetDBGState(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +DNBArchMachARM::SetRegisterState(int set) +{ + // Make sure we have a valid context to set. + kern_return_t err = GetRegisterState(set, false); + if (err != KERN_SUCCESS) + return err; + + switch (set) + { + case e_regSetALL: return SetGPRState() | + SetVFPState() | + SetEXCState() | + SetDBGState(); + case e_regSetGPR: return SetGPRState(); + case e_regSetVFP: return SetVFPState(); + case e_regSetEXC: return SetEXCState(); + case e_regSetDBG: return SetDBGState(); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +bool +DNBArchMachARM::RegisterSetStateIsValid (int set) const +{ + return m_state.RegsAreValid(set); +} + + +#endif // #if defined (__arm__) + diff --git a/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h new file mode 100644 index 000000000000..f40c891ce98c --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h @@ -0,0 +1,217 @@ +//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DebugNubArchMachARM_h__ +#define __DebugNubArchMachARM_h__ + +#if defined (__arm__) + +#include "DNBArch.h" +#include + +class MachThread; + +class DNBArchMachARM : public DNBArchProtocol +{ +public: + enum { kMaxNumThumbITBreakpoints = 4 }; + + DNBArchMachARM(MachThread *thread) : + m_thread(thread), + m_state(), + m_hw_single_chained_step_addr(INVALID_NUB_ADDRESS), + m_sw_single_step_next_pc(INVALID_NUB_ADDRESS), + m_sw_single_step_break_id(INVALID_NUB_BREAK_ID), + m_sw_single_step_itblock_break_count(0), + m_last_decode_pc(INVALID_NUB_ADDRESS) + { + memset(&m_dbg_save, 0, sizeof(m_dbg_save)); + ThumbStaticsInit(&m_last_decode_thumb); + for (int i = 0; i < kMaxNumThumbITBreakpoints; i++) + m_sw_single_step_itblock_break_id[i] = INVALID_NUB_BREAK_ID; + } + + virtual ~DNBArchMachARM() + { + } + + virtual const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets) const; + virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *value); + virtual bool SetRegisterValue(int set, int reg, const DNBRegisterValue *value); + + virtual kern_return_t GetRegisterState (int set, bool force); + virtual kern_return_t SetRegisterState (int set); + virtual bool RegisterSetStateIsValid (int set) const; + + virtual uint64_t GetPC(uint64_t failValue); // Get program counter + virtual kern_return_t SetPC(uint64_t value); + virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer + virtual void ThreadWillResume(); + virtual bool ThreadDidStop(); + + static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size); + static uint32_t GetCPUType(); + + virtual uint32_t NumSupportedHardwareBreakpoints(); + virtual uint32_t NumSupportedHardwareWatchpoints(); + virtual uint32_t EnableHardwareBreakpoint (nub_addr_t addr, nub_size_t size); + virtual uint32_t EnableHardwareWatchpoint (nub_addr_t addr, nub_size_t size, bool read, bool write); + virtual bool DisableHardwareBreakpoint (uint32_t hw_break_index); + virtual bool DisableHardwareWatchpoint (uint32_t hw_break_index); + virtual bool StepNotComplete (); + +protected: + + + kern_return_t EnableHardwareSingleStep (bool enable); + kern_return_t SetSingleStepSoftwareBreakpoints (); + + bool ConditionPassed(uint8_t condition, uint32_t cpsr); + bool ComputeNextPC(nub_addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, nub_addr_t *targetPC); + void EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, nub_addr_t *nextPC, bool *nextPCIsThumb); + void DecodeITBlockInstructions(nub_addr_t curr_pc); + arm_error_t DecodeInstructionUsingDisassembler(nub_addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, nub_addr_t *next_pc); + static nub_bool_t BreakpointHit (nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton); + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR = ARM_THREAD_STATE, + e_regSetVFP = ARM_VFP_STATE, + e_regSetEXC = ARM_EXCEPTION_STATE, + e_regSetDBG = ARM_DEBUG_STATE, + kNumRegisterSets + } RegisterSet; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + struct State + { + arm_thread_state_t gpr; + arm_vfp_state_t vfp; + arm_exception_state_t exc; + arm_debug_state_t dbg; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t vfp_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + kern_return_t dbg_errs[2]; // Read/Write errors + State() + { + uint32_t i; + for (i=0; i diff --git a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp new file mode 100644 index 000000000000..0a0601f63781 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp @@ -0,0 +1,879 @@ +//===-- DNBArchImplI386.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#if defined (__i386__) + +#include + +#include "MacOSX/i386/DNBArchImplI386.h" +#include "DNBLog.h" +#include "MachThread.h" +#include "MachProcess.h" + +static const uint8_t g_breakpoint_opcode[] = { 0xCC }; + +enum +{ + gpr_eax = 0, + gpr_ebx = 1, + gpr_ecx = 2, + gpr_edx = 3, + gpr_edi = 4, + gpr_esi = 5, + gpr_ebp = 6, + gpr_esp = 7, + gpr_ss = 8, + gpr_eflags = 9, + gpr_eip = 10, + gpr_cs = 11, + gpr_ds = 12, + gpr_es = 13, + gpr_fs = 14, + gpr_gs = 15, + k_num_gpr_regs +}; + +enum { + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + k_num_fpu_regs, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum { + exc_trapno, + exc_err, + exc_faultvaddr, + k_num_exc_regs, +}; + + +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 +}; + +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 +}; + + +const uint8_t * const +DNBArchImplI386::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + if (byte_size == 1) + return g_breakpoint_opcode; + return NULL; +} + +uint32_t +DNBArchImplI386::GetCPUType() +{ + return CPU_TYPE_I386; +} + +uint64_t +DNBArchImplI386::GetPC(uint64_t failValue) +{ + // Get program counter + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.context.gpr.__eip; + return failValue; +} + +kern_return_t +DNBArchImplI386::SetPC(uint64_t value) +{ + // Get program counter + kern_return_t err = GetGPRState(false); + if (err == KERN_SUCCESS) + { + m_state.context.gpr.__eip = value; + err = SetGPRState(); + } + return err == KERN_SUCCESS; +} + +uint64_t +DNBArchImplI386::GetSP(uint64_t failValue) +{ + // Get stack pointer + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.context.gpr.__esp; + return failValue; +} + +// Uncomment the value below to verify the values in the debugger. +//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED +//#define SET_GPR(reg) m_state.context.gpr.__##reg = gpr_##reg + +kern_return_t +DNBArchImplI386::GetGPRState(bool force) +{ + if (force || m_state.GetError(e_regSetGPR, Read)) + { +#if DEBUG_GPR_VALUES + SET_GPR(eax); + SET_GPR(ebx); + SET_GPR(ecx); + SET_GPR(edx); + SET_GPR(edi); + SET_GPR(esi); + SET_GPR(ebp); + SET_GPR(esp); + SET_GPR(ss); + SET_GPR(eflags); + SET_GPR(eip); + SET_GPR(cs); + SET_GPR(ds); + SET_GPR(es); + SET_GPR(fs); + SET_GPR(gs); + m_state.SetError(e_regSetGPR, Read, 0); +#else + mach_msg_type_number_t count = e_regSetWordSizeGPR; + m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->ThreadID(), x86_THREAD_STATE32, (thread_state_t)&m_state.context.gpr, &count)); +#endif + } + return m_state.GetError(e_regSetGPR, Read); +} + +// Uncomment the value below to verify the values in the debugger. +//#define DEBUG_FPU_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED + +kern_return_t +DNBArchImplI386::GetFPUState(bool force) +{ + if (force || m_state.GetError(e_regSetFPU, Read)) + { +#if DEBUG_FPU_VALUES + m_state.context.fpu.__fpu_reserved[0] = -1; + m_state.context.fpu.__fpu_reserved[1] = -1; + *(uint16_t *)&(m_state.context.fpu.__fpu_fcw) = 0x1234; + *(uint16_t *)&(m_state.context.fpu.__fpu_fsw) = 0x5678; + m_state.context.fpu.__fpu_ftw = 1; + m_state.context.fpu.__fpu_rsrv1 = UINT8_MAX; + m_state.context.fpu.__fpu_fop = 2; + m_state.context.fpu.__fpu_ip = 3; + m_state.context.fpu.__fpu_cs = 4; + m_state.context.fpu.__fpu_rsrv2 = 5; + m_state.context.fpu.__fpu_dp = 6; + m_state.context.fpu.__fpu_ds = 7; + m_state.context.fpu.__fpu_rsrv3 = UINT16_MAX; + m_state.context.fpu.__fpu_mxcsr = 8; + m_state.context.fpu.__fpu_mxcsrmask = 9; + int i; + for (i=0; i<16; ++i) + { + if (i<10) + { + m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = 'a'; + m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = 'b'; + m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = 'c'; + m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = 'd'; + m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = 'e'; + m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = 'f'; + m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = 'g'; + m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = 'h'; + } + else + { + m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = INT8_MIN; + } + + m_state.context.fpu.__fpu_xmm0.__xmm_reg[i] = '0'; + m_state.context.fpu.__fpu_xmm1.__xmm_reg[i] = '1'; + m_state.context.fpu.__fpu_xmm2.__xmm_reg[i] = '2'; + m_state.context.fpu.__fpu_xmm3.__xmm_reg[i] = '3'; + m_state.context.fpu.__fpu_xmm4.__xmm_reg[i] = '4'; + m_state.context.fpu.__fpu_xmm5.__xmm_reg[i] = '5'; + m_state.context.fpu.__fpu_xmm6.__xmm_reg[i] = '6'; + m_state.context.fpu.__fpu_xmm7.__xmm_reg[i] = '7'; + } + for (i=0; iThreadID(), x86_FLOAT_STATE32, (thread_state_t)&m_state.context.fpu, &count)); +#endif + } + return m_state.GetError(e_regSetFPU, Read); +} + +kern_return_t +DNBArchImplI386::GetEXCState(bool force) +{ + if (force || m_state.GetError(e_regSetEXC, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeEXC; + m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->ThreadID(), x86_EXCEPTION_STATE32, (thread_state_t)&m_state.context.exc, &count)); + } + return m_state.GetError(e_regSetEXC, Read); +} + +kern_return_t +DNBArchImplI386::SetGPRState() +{ + m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->ThreadID(), x86_THREAD_STATE32, (thread_state_t)&m_state.context.gpr, e_regSetWordSizeGPR)); + return m_state.GetError(e_regSetGPR, Write); +} + +kern_return_t +DNBArchImplI386::SetFPUState() +{ + m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->ThreadID(), x86_FLOAT_STATE32, (thread_state_t)&m_state.context.fpu, e_regSetWordSizeFPR)); + return m_state.GetError(e_regSetFPU, Write); +} + +kern_return_t +DNBArchImplI386::SetEXCState() +{ + m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->ThreadID(), x86_EXCEPTION_STATE32, (thread_state_t)&m_state.context.exc, e_regSetWordSizeEXC)); + return m_state.GetError(e_regSetEXC, Write); +} + +void +DNBArchImplI386::ThreadWillResume() +{ + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread->IsStepping()) + { + // This is the primary thread, let the arch do anything it needs + EnableHardwareSingleStep(true) == KERN_SUCCESS; + } +} + +bool +DNBArchImplI386::ThreadDidStop() +{ + bool success = true; + + m_state.InvalidateAllRegisterStates(); + + // Are we stepping a single instruction? + if (GetGPRState(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread->IsStepping()) + { + // This was the primary thread, we need to clear the trace + // bit if so. + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + } + else + { + // The MachThread will automatically restore the suspend count + // in ThreadDidStop(), so we don't need to do anything here if + // we weren't the primary thread the last time + } + } + return success; +} + +bool +DNBArchImplI386::NotifyException(MachException::Data& exc) +{ + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) + { + nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS); + if (pc != INVALID_NUB_ADDRESS && pc > 0) + { + pc -= 1; + // Check for a breakpoint at one byte prior to the current PC value + // since the PC will be just past the trap. + + nub_break_t breakID = m_thread->Process()->Breakpoints().FindIDByAddress(pc); + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + // Backup the PC for i386 since the trap was taken and the PC + // is at the address following the single byte trap instruction. + if (m_state.context.gpr.__eip > 0) + { + m_state.context.gpr.__eip = pc; + // Write the new PC back out + SetGPRState (); + } + + m_thread->SetCurrentBreakpoint(breakID); + } + return true; + } + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + + +// Set the single step bit in the processor status register. +kern_return_t +DNBArchImplI386::EnableHardwareSingleStep (bool enable) +{ + if (GetGPRState(false) == KERN_SUCCESS) + { + const uint32_t trace_bit = 0x100u; + if (enable) + m_state.context.gpr.__eflags |= trace_bit; + else + m_state.context.gpr.__eflags &= ~trace_bit; + return SetGPRState(); + } + return m_state.GetError(e_regSetGPR, Read); +} + + +//---------------------------------------------------------------------- +// Register information defintions +//---------------------------------------------------------------------- + + +#define GPR_OFFSET(reg) (offsetof (DNBArchImplI386::GPR, __##reg)) +#define FPU_OFFSET(reg) (offsetof (DNBArchImplI386::FPU, __fpu_##reg) + offsetof (DNBArchImplI386::Context, fpu)) +#define EXC_OFFSET(reg) (offsetof (DNBArchImplI386::EXC, __##reg) + offsetof (DNBArchImplI386::Context, exc)) + +#define GPR_SIZE(reg) (sizeof(((DNBArchImplI386::GPR *)NULL)->__##reg)) +#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg)) +#define FPU_SIZE_MMST(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__mmst_reg)) +#define FPU_SIZE_XMM(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__xmm_reg)) +#define EXC_SIZE(reg) (sizeof(((DNBArchImplI386::EXC *)NULL)->__##reg)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that +// the register state structures are defined correctly and have the correct +// sizes and offsets. + +// General purpose registers for 64 bit +const DNBRegisterInfo +DNBArchImplI386::g_gpr_registers[] = +{ +{ e_regSetGPR, gpr_eax, "eax" , NULL , Uint, Hex, GPR_SIZE(eax), GPR_OFFSET(eax) , gcc_eax , dwarf_eax , -1 , gdb_eax }, +{ e_regSetGPR, gpr_ebx, "ebx" , NULL , Uint, Hex, GPR_SIZE(ebx), GPR_OFFSET(ebx) , gcc_ebx , dwarf_ebx , -1 , gdb_ebx }, +{ e_regSetGPR, gpr_ecx, "ecx" , NULL , Uint, Hex, GPR_SIZE(ecx), GPR_OFFSET(ecx) , gcc_ecx , dwarf_ecx , -1 , gdb_ecx }, +{ e_regSetGPR, gpr_edx, "edx" , NULL , Uint, Hex, GPR_SIZE(edx), GPR_OFFSET(edx) , gcc_edx , dwarf_edx , -1 , gdb_edx }, +{ e_regSetGPR, gpr_edi, "edi" , NULL , Uint, Hex, GPR_SIZE(edi), GPR_OFFSET(edi) , gcc_edi , dwarf_edi , -1 , gdb_edi }, +{ e_regSetGPR, gpr_esi, "esi" , NULL , Uint, Hex, GPR_SIZE(esi), GPR_OFFSET(esi) , gcc_esi , dwarf_esi , -1 , gdb_esi }, +{ e_regSetGPR, gpr_ebp, "ebp" , "fp" , Uint, Hex, GPR_SIZE(ebp), GPR_OFFSET(ebp) , gcc_ebp , dwarf_ebp , GENERIC_REGNUM_FP , gdb_ebp }, +{ e_regSetGPR, gpr_esp, "esp" , "sp" , Uint, Hex, GPR_SIZE(esp), GPR_OFFSET(esp) , gcc_esp , dwarf_esp , GENERIC_REGNUM_SP , gdb_esp }, +{ e_regSetGPR, gpr_ss, "ss" , NULL , Uint, Hex, GPR_SIZE(ss), GPR_OFFSET(ss) , -1 , -1 , -1 , gdb_ss }, +{ e_regSetGPR, gpr_eflags, "eflags", "flags" , Uint, Hex, GPR_SIZE(eflags), GPR_OFFSET(eflags) , gcc_eflags, dwarf_eflags , GENERIC_REGNUM_FLAGS , gdb_eflags}, +{ e_regSetGPR, gpr_eip, "eip" , "pc" , Uint, Hex, GPR_SIZE(eip), GPR_OFFSET(eip) , gcc_eip , dwarf_eip , GENERIC_REGNUM_PC , gdb_eip }, +{ e_regSetGPR, gpr_cs, "cs" , NULL , Uint, Hex, GPR_SIZE(cs), GPR_OFFSET(cs) , -1 , -1 , -1 , gdb_cs }, +{ e_regSetGPR, gpr_ds, "ds" , NULL , Uint, Hex, GPR_SIZE(ds), GPR_OFFSET(ds) , -1 , -1 , -1 , gdb_ds }, +{ e_regSetGPR, gpr_es, "es" , NULL , Uint, Hex, GPR_SIZE(es), GPR_OFFSET(es) , -1 , -1 , -1 , gdb_es }, +{ e_regSetGPR, gpr_fs, "fs" , NULL , Uint, Hex, GPR_SIZE(fs), GPR_OFFSET(fs) , -1 , -1 , -1 , gdb_fs }, +{ e_regSetGPR, gpr_gs, "gs" , NULL , Uint, Hex, GPR_SIZE(gs), GPR_OFFSET(gs) , -1 , -1 , -1 , gdb_gs } +}; + + +const DNBRegisterInfo +DNBArchImplI386::g_fpu_registers[] = +{ +{ e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , -1, -1, -1, -1 }, +{ e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , -1, -1, -1, -1 }, + +{ e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), -1, dwarf_stmm0, -1, gdb_stmm0 }, +{ e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), -1, dwarf_stmm1, -1, gdb_stmm1 }, +{ e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), -1, dwarf_stmm2, -1, gdb_stmm2 }, +{ e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), -1, dwarf_stmm3, -1, gdb_stmm3 }, +{ e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), -1, dwarf_stmm4, -1, gdb_stmm4 }, +{ e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), -1, dwarf_stmm5, -1, gdb_stmm5 }, +{ e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), -1, dwarf_stmm6, -1, gdb_stmm6 }, +{ e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), -1, dwarf_stmm7, -1, gdb_stmm7 }, + +{ e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0), FPU_OFFSET(xmm0), -1, dwarf_xmm0, -1, gdb_xmm0 }, +{ e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1), FPU_OFFSET(xmm1), -1, dwarf_xmm1, -1, gdb_xmm1 }, +{ e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2), FPU_OFFSET(xmm2), -1, dwarf_xmm2, -1, gdb_xmm2 }, +{ e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3), FPU_OFFSET(xmm3), -1, dwarf_xmm3, -1, gdb_xmm3 }, +{ e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4), FPU_OFFSET(xmm4), -1, dwarf_xmm4, -1, gdb_xmm4 }, +{ e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5), FPU_OFFSET(xmm5), -1, dwarf_xmm5, -1, gdb_xmm5 }, +{ e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6), FPU_OFFSET(xmm6), -1, dwarf_xmm6, -1, gdb_xmm6 }, +{ e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7), FPU_OFFSET(xmm7), -1, dwarf_xmm7, -1, gdb_xmm7 } +}; + + + +const DNBRegisterInfo +DNBArchImplI386::g_exc_registers[] = +{ +{ e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , -1, -1, -1, -1 }, +{ e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , -1, -1, -1, -1 }, +{ e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , -1, -1, -1, -1 } +}; + +// Number of registers in each register set +const size_t DNBArchImplI386::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplI386::k_num_fpu_registers = sizeof(g_fpu_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplI386::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplI386::k_num_all_registers = k_num_gpr_registers + k_num_fpu_registers + k_num_exc_registers; + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +const DNBRegisterSetInfo +DNBArchImplI386::g_reg_sets[] = +{ + { "i386 Registers", NULL, k_num_all_registers }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpu_registers, k_num_fpu_registers }, + { "Exception State Registers", g_exc_registers, k_num_exc_registers } +}; +// Total number of register sets for this architecture +const size_t DNBArchImplI386::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo); + + +const DNBRegisterSetInfo * +DNBArchImplI386::GetRegisterSetInfo(nub_size_t *num_reg_sets) +{ + *num_reg_sets = k_num_register_sets; + return g_reg_sets; +} + +bool +DNBArchImplI386::GetRegisterValue(int set, int reg, DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_eip; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_esp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_ebp; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_eflags; + break; + + case GENERIC_REGNUM_RA: // Return Address + default: + return false; + } + } + + if (GetRegisterState(set, false) != KERN_SUCCESS) + return false; + + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + value->info = *regInfo; + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + value->value.uint32 = ((uint32_t*)(&m_state.context.gpr))[reg]; + return true; + } + break; + + case e_regSetFPU: + switch (reg) + { + case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)); return true; + case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)); return true; + case fpu_ftw: value->value.uint8 = m_state.context.fpu.__fpu_ftw; return true; + case fpu_fop: value->value.uint16 = m_state.context.fpu.__fpu_fop; return true; + case fpu_ip: value->value.uint32 = m_state.context.fpu.__fpu_ip; return true; + case fpu_cs: value->value.uint16 = m_state.context.fpu.__fpu_cs; return true; + case fpu_dp: value->value.uint32 = m_state.context.fpu.__fpu_dp; return true; + case fpu_ds: value->value.uint16 = m_state.context.fpu.__fpu_ds; return true; + case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.__fpu_mxcsr; return true; + case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.__fpu_mxcsrmask; return true; + + case fpu_stmm0: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm0.__mmst_reg, 10); return true; + case fpu_stmm1: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm1.__mmst_reg, 10); return true; + case fpu_stmm2: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm2.__mmst_reg, 10); return true; + case fpu_stmm3: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm3.__mmst_reg, 10); return true; + case fpu_stmm4: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm4.__mmst_reg, 10); return true; + case fpu_stmm5: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm5.__mmst_reg, 10); return true; + case fpu_stmm6: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm6.__mmst_reg, 10); return true; + case fpu_stmm7: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_stmm7.__mmst_reg, 10); return true; + + case fpu_xmm0: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm0.__xmm_reg, 16); return true; + case fpu_xmm1: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm1.__xmm_reg, 16); return true; + case fpu_xmm2: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm2.__xmm_reg, 16); return true; + case fpu_xmm3: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm3.__xmm_reg, 16); return true; + case fpu_xmm4: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm4.__xmm_reg, 16); return true; + case fpu_xmm5: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm5.__xmm_reg, 16); return true; + case fpu_xmm6: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm6.__xmm_reg, 16); return true; + case fpu_xmm7: memcpy(&value->value.uint8, m_state.context.fpu.__fpu_xmm7.__xmm_reg, 16); return true; + } + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + value->value.uint32 = (&m_state.context.exc.__trapno)[reg]; + return true; + } + break; + } + } + return false; +} + + +bool +DNBArchImplI386::SetRegisterValue(int set, int reg, const DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_eip; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_esp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_ebp; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_eflags; + break; + + case GENERIC_REGNUM_RA: // Return Address + default: + return false; + } + } + + if (GetRegisterState(set, false) != KERN_SUCCESS) + return false; + + bool success = false; + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + ((uint32_t*)(&m_state.context.gpr))[reg] = value->value.uint32; + success = true; + } + break; + + case e_regSetFPU: + switch (reg) + { + case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)) = value->value.uint16; success = true; break; + case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)) = value->value.uint16; success = true; break; + case fpu_ftw: m_state.context.fpu.__fpu_ftw = value->value.uint8; success = true; break; + case fpu_fop: m_state.context.fpu.__fpu_fop = value->value.uint16; success = true; break; + case fpu_ip: m_state.context.fpu.__fpu_ip = value->value.uint32; success = true; break; + case fpu_cs: m_state.context.fpu.__fpu_cs = value->value.uint16; success = true; break; + case fpu_dp: m_state.context.fpu.__fpu_dp = value->value.uint32; success = true; break; + case fpu_ds: m_state.context.fpu.__fpu_ds = value->value.uint16; success = true; break; + case fpu_mxcsr: m_state.context.fpu.__fpu_mxcsr = value->value.uint32; success = true; break; + case fpu_mxcsrmask: m_state.context.fpu.__fpu_mxcsrmask = value->value.uint32; success = true; break; + + case fpu_stmm0: memcpy (m_state.context.fpu.__fpu_stmm0.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm1: memcpy (m_state.context.fpu.__fpu_stmm1.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm2: memcpy (m_state.context.fpu.__fpu_stmm2.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm3: memcpy (m_state.context.fpu.__fpu_stmm3.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm4: memcpy (m_state.context.fpu.__fpu_stmm4.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm5: memcpy (m_state.context.fpu.__fpu_stmm5.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm6: memcpy (m_state.context.fpu.__fpu_stmm6.__mmst_reg, &value->value.uint8, 10); success = true; break; + case fpu_stmm7: memcpy (m_state.context.fpu.__fpu_stmm7.__mmst_reg, &value->value.uint8, 10); success = true; break; + + case fpu_xmm0: memcpy(m_state.context.fpu.__fpu_xmm0.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm1: memcpy(m_state.context.fpu.__fpu_xmm1.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm2: memcpy(m_state.context.fpu.__fpu_xmm2.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm3: memcpy(m_state.context.fpu.__fpu_xmm3.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm4: memcpy(m_state.context.fpu.__fpu_xmm4.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm5: memcpy(m_state.context.fpu.__fpu_xmm5.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm6: memcpy(m_state.context.fpu.__fpu_xmm6.__xmm_reg, &value->value.uint8, 16); success = true; break; + case fpu_xmm7: memcpy(m_state.context.fpu.__fpu_xmm7.__xmm_reg, &value->value.uint8, 16); success = true; break; + } + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + (&m_state.context.exc.__trapno)[reg] = value->value.uint32; + success = true; + } + break; + } + } + + if (success) + return SetRegisterState(set) == KERN_SUCCESS; + return false; +} + + +nub_size_t +DNBArchImplI386::GetRegisterContext (void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context); + + if (buf && buf_len) + { + if (size > buf_len) + size = buf_len; + + bool force = false; + if (GetGPRState(force) | GetFPUState(force) | GetEXCState(force)) + return 0; + ::memcpy (buf, &m_state.context, size); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size); + // Return the size of the register context even if NULL was passed in + return size; +} + +nub_size_t +DNBArchImplI386::SetRegisterContext (const void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context); + if (buf == NULL || buf_len == 0) + size = 0; + + if (size) + { + if (size > buf_len) + size = buf_len; + + ::memcpy (&m_state.context, buf, size); + SetGPRState(); + SetFPUState(); + SetEXCState(); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size); + return size; +} + + + +kern_return_t +DNBArchImplI386::GetRegisterState(int set, bool force) +{ + switch (set) + { + case e_regSetALL: return GetGPRState(force) | GetFPUState(force) | GetEXCState(force); + case e_regSetGPR: return GetGPRState(force); + case e_regSetFPU: return GetFPUState(force); + case e_regSetEXC: return GetEXCState(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +DNBArchImplI386::SetRegisterState(int set) +{ + // Make sure we have a valid context to set. + if (RegisterSetStateIsValid(set)) + { + switch (set) + { + case e_regSetALL: return SetGPRState() | SetFPUState() | SetEXCState(); + case e_regSetGPR: return SetGPRState(); + case e_regSetFPU: return SetFPUState(); + case e_regSetEXC: return SetEXCState(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +bool +DNBArchImplI386::RegisterSetStateIsValid (int set) const +{ + return m_state.RegsAreValid(set); +} + + + +#endif // #if defined (__i386__) diff --git a/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h new file mode 100644 index 000000000000..10972c24ca5b --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h @@ -0,0 +1,196 @@ +//===-- DNBArchImplI386.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBArchImplI386_h__ +#define __DNBArchImplI386_h__ + +#if defined (__i386__) + +#include "DNBArch.h" +#include +#include + + +class MachThread; + +class DNBArchImplI386 : public DNBArchProtocol +{ +public: + DNBArchImplI386(MachThread *thread) : + m_thread(thread), + m_state() + { + } + virtual ~DNBArchImplI386() + { + } + + static const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets); + + virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *reg); + virtual bool SetRegisterValue(int set, int reg, const DNBRegisterValue *reg); + virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len); + virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len); + virtual kern_return_t GetRegisterState (int set, bool force); + virtual kern_return_t SetRegisterState (int set); + virtual bool RegisterSetStateIsValid (int set) const; + + virtual uint64_t GetPC(uint64_t failValue); // Get program counter + virtual kern_return_t SetPC(uint64_t value); + virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer + virtual void ThreadWillResume(); + virtual bool ThreadDidStop(); + virtual bool NotifyException(MachException::Data& exc); + + static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size); + static uint32_t GetCPUType(); + +protected: + kern_return_t EnableHardwareSingleStep (bool enable); + + typedef i386_thread_state_t GPR; + typedef i386_float_state_t FPU; + typedef i386_exception_state_t EXC; + + static const DNBRegisterInfo g_gpr_registers[]; + static const DNBRegisterInfo g_fpu_registers[]; + static const DNBRegisterInfo g_exc_registers[]; + static const DNBRegisterSetInfo g_reg_sets[]; + static const size_t k_num_gpr_registers; + static const size_t k_num_fpu_registers; + static const size_t k_num_exc_registers; + static const size_t k_num_all_registers; + static const size_t k_num_register_sets; + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPU, + e_regSetEXC, + kNumRegisterSets + } RegisterSet; + + typedef enum RegisterSetWordSizeTag + { + e_regSetWordSizeGPR = i386_THREAD_STATE_COUNT, + e_regSetWordSizeFPR = i386_FLOAT_STATE_COUNT, + e_regSetWordSizeEXC = i386_EXCEPTION_STATE_COUNT + } RegisterSetWordSize; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + struct Context + { + i386_thread_state_t gpr; + i386_float_state_t fpu; + i386_exception_state_t exc; + }; + + struct State + { + Context context; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + + State() + { + uint32_t i; + for (i=0; iThreadID(), e_regSetGPR, (thread_state_t)&m_state.gpr, &count)); + } + return m_state.GetError(e_regSetGPR, Read); +} + +kern_return_t +DNBArchMachPPC::GetFPRState(bool force) +{ + if (force || m_state.GetError(e_regSetFPR, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeFPR; + m_state.SetError(e_regSetFPR, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetFPR, (thread_state_t)&m_state.fpr, &count)); + } + return m_state.GetError(e_regSetFPR, Read); +} + +kern_return_t +DNBArchMachPPC::GetEXCState(bool force) +{ + if (force || m_state.GetError(e_regSetEXC, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeEXC; + m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetEXC, (thread_state_t)&m_state.exc, &count)); + } + return m_state.GetError(e_regSetEXC, Read); +} + +kern_return_t +DNBArchMachPPC::GetVECState(bool force) +{ + if (force || m_state.GetError(e_regSetVEC, Read)) + { + mach_msg_type_number_t count = e_regSetWordSizeVEC; + m_state.SetError(e_regSetVEC, Read, ::thread_get_state(m_thread->ThreadID(), e_regSetVEC, (thread_state_t)&m_state.vec, &count)); + } + return m_state.GetError(e_regSetVEC, Read); +} + +kern_return_t +DNBArchMachPPC::SetGPRState() +{ + m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetGPR, (thread_state_t)&m_state.gpr, e_regSetWordSizeGPR)); + return m_state.GetError(e_regSetGPR, Write); +} + +kern_return_t +DNBArchMachPPC::SetFPRState() +{ + m_state.SetError(e_regSetFPR, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetFPR, (thread_state_t)&m_state.fpr, e_regSetWordSizeFPR)); + return m_state.GetError(e_regSetFPR, Write); +} + +kern_return_t +DNBArchMachPPC::SetEXCState() +{ + m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetEXC, (thread_state_t)&m_state.exc, e_regSetWordSizeEXC)); + return m_state.GetError(e_regSetEXC, Write); +} + +kern_return_t +DNBArchMachPPC::SetVECState() +{ + m_state.SetError(e_regSetVEC, Write, ::thread_set_state(m_thread->ThreadID(), e_regSetVEC, (thread_state_t)&m_state.vec, e_regSetWordSizeVEC)); + return m_state.GetError(e_regSetVEC, Write); +} + +bool +DNBArchMachPPC::ThreadWillResume() +{ + bool success = true; + + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread->IsStepping()) + { + // This is the primary thread, let the arch do anything it needs + success = EnableHardwareSingleStep(true) == KERN_SUCCESS; + } + return success; +} + +bool +DNBArchMachPPC::ThreadDidStop() +{ + bool success = true; + + m_state.InvalidateAllRegisterStates(); + + // Are we stepping a single instruction? + if (GetGPRState(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread->IsStepping()) + { + // This was the primary thread, we need to clear the trace + // bit if so. + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + } + else + { + // The MachThread will automatically restore the suspend count + // in ThreadDidStop(), so we don't need to do anything here if + // we weren't the primary thread the last time + } + } + return success; +} + + +// Set the single step bit in the processor status register. +kern_return_t +DNBArchMachPPC::EnableHardwareSingleStep (bool enable) +{ + DNBLogThreadedIf(LOG_STEP, "DNBArchMachPPC::EnableHardwareSingleStep( enable = %d )", enable); + if (GetGPRState(false) == KERN_SUCCESS) + { + const uint32_t trace_bit = 0x400; + if (enable) + m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) |= trace_bit; + else + m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) &= ~trace_bit; + return SetGPRState(); + } + return m_state.GetError(e_regSetGPR, Read); +} + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit PowerPC. +//---------------------------------------------------------------------- + +enum gpr_regnums +{ + e_regNumGPR_srr0, + e_regNumGPR_srr1, + e_regNumGPR_r0, + e_regNumGPR_r1, + e_regNumGPR_r2, + e_regNumGPR_r3, + e_regNumGPR_r4, + e_regNumGPR_r5, + e_regNumGPR_r6, + e_regNumGPR_r7, + e_regNumGPR_r8, + e_regNumGPR_r9, + e_regNumGPR_r10, + e_regNumGPR_r11, + e_regNumGPR_r12, + e_regNumGPR_r13, + e_regNumGPR_r14, + e_regNumGPR_r15, + e_regNumGPR_r16, + e_regNumGPR_r17, + e_regNumGPR_r18, + e_regNumGPR_r19, + e_regNumGPR_r20, + e_regNumGPR_r21, + e_regNumGPR_r22, + e_regNumGPR_r23, + e_regNumGPR_r24, + e_regNumGPR_r25, + e_regNumGPR_r26, + e_regNumGPR_r27, + e_regNumGPR_r28, + e_regNumGPR_r29, + e_regNumGPR_r30, + e_regNumGPR_r31, + e_regNumGPR_cr, + e_regNumGPR_xer, + e_regNumGPR_lr, + e_regNumGPR_ctr, + e_regNumGPR_mq, + e_regNumGPR_vrsave +}; + + + + +// General purpose registers +static DNBRegisterInfo g_gpr_registers[] = +{ + { "srr0" , Uint, 4, Hex }, + { "srr1" , Uint, 4, Hex }, + { "r0" , Uint, 4, Hex }, + { "r1" , Uint, 4, Hex }, + { "r2" , Uint, 4, Hex }, + { "r3" , Uint, 4, Hex }, + { "r4" , Uint, 4, Hex }, + { "r5" , Uint, 4, Hex }, + { "r6" , Uint, 4, Hex }, + { "r7" , Uint, 4, Hex }, + { "r8" , Uint, 4, Hex }, + { "r9" , Uint, 4, Hex }, + { "r10" , Uint, 4, Hex }, + { "r11" , Uint, 4, Hex }, + { "r12" , Uint, 4, Hex }, + { "r13" , Uint, 4, Hex }, + { "r14" , Uint, 4, Hex }, + { "r15" , Uint, 4, Hex }, + { "r16" , Uint, 4, Hex }, + { "r17" , Uint, 4, Hex }, + { "r18" , Uint, 4, Hex }, + { "r19" , Uint, 4, Hex }, + { "r20" , Uint, 4, Hex }, + { "r21" , Uint, 4, Hex }, + { "r22" , Uint, 4, Hex }, + { "r23" , Uint, 4, Hex }, + { "r24" , Uint, 4, Hex }, + { "r25" , Uint, 4, Hex }, + { "r26" , Uint, 4, Hex }, + { "r27" , Uint, 4, Hex }, + { "r28" , Uint, 4, Hex }, + { "r29" , Uint, 4, Hex }, + { "r30" , Uint, 4, Hex }, + { "r31" , Uint, 4, Hex }, + { "cr" , Uint, 4, Hex }, + { "xer" , Uint, 4, Hex }, + { "lr" , Uint, 4, Hex }, + { "ctr" , Uint, 4, Hex }, + { "mq" , Uint, 4, Hex }, + { "vrsave", Uint, 4, Hex }, +}; + +// Floating point registers +static DNBRegisterInfo g_fpr_registers[] = +{ + { "fp0" , IEEE754, 8, Float }, + { "fp1" , IEEE754, 8, Float }, + { "fp2" , IEEE754, 8, Float }, + { "fp3" , IEEE754, 8, Float }, + { "fp4" , IEEE754, 8, Float }, + { "fp5" , IEEE754, 8, Float }, + { "fp6" , IEEE754, 8, Float }, + { "fp7" , IEEE754, 8, Float }, + { "fp8" , IEEE754, 8, Float }, + { "fp9" , IEEE754, 8, Float }, + { "fp10" , IEEE754, 8, Float }, + { "fp11" , IEEE754, 8, Float }, + { "fp12" , IEEE754, 8, Float }, + { "fp13" , IEEE754, 8, Float }, + { "fp14" , IEEE754, 8, Float }, + { "fp15" , IEEE754, 8, Float }, + { "fp16" , IEEE754, 8, Float }, + { "fp17" , IEEE754, 8, Float }, + { "fp18" , IEEE754, 8, Float }, + { "fp19" , IEEE754, 8, Float }, + { "fp20" , IEEE754, 8, Float }, + { "fp21" , IEEE754, 8, Float }, + { "fp22" , IEEE754, 8, Float }, + { "fp23" , IEEE754, 8, Float }, + { "fp24" , IEEE754, 8, Float }, + { "fp25" , IEEE754, 8, Float }, + { "fp26" , IEEE754, 8, Float }, + { "fp27" , IEEE754, 8, Float }, + { "fp28" , IEEE754, 8, Float }, + { "fp29" , IEEE754, 8, Float }, + { "fp30" , IEEE754, 8, Float }, + { "fp31" , IEEE754, 8, Float }, + { "fpscr" , Uint, 4, Hex } +}; + +// Exception registers + +static DNBRegisterInfo g_exc_registers[] = +{ + { "dar" , Uint, 4, Hex }, + { "dsisr" , Uint, 4, Hex }, + { "exception" , Uint, 4, Hex } +}; + +// Altivec registers +static DNBRegisterInfo g_vec_registers[] = +{ + { "vr0" , Vector, 16, VectorOfFloat32 }, + { "vr1" , Vector, 16, VectorOfFloat32 }, + { "vr2" , Vector, 16, VectorOfFloat32 }, + { "vr3" , Vector, 16, VectorOfFloat32 }, + { "vr4" , Vector, 16, VectorOfFloat32 }, + { "vr5" , Vector, 16, VectorOfFloat32 }, + { "vr6" , Vector, 16, VectorOfFloat32 }, + { "vr7" , Vector, 16, VectorOfFloat32 }, + { "vr8" , Vector, 16, VectorOfFloat32 }, + { "vr9" , Vector, 16, VectorOfFloat32 }, + { "vr10" , Vector, 16, VectorOfFloat32 }, + { "vr11" , Vector, 16, VectorOfFloat32 }, + { "vr12" , Vector, 16, VectorOfFloat32 }, + { "vr13" , Vector, 16, VectorOfFloat32 }, + { "vr14" , Vector, 16, VectorOfFloat32 }, + { "vr15" , Vector, 16, VectorOfFloat32 }, + { "vr16" , Vector, 16, VectorOfFloat32 }, + { "vr17" , Vector, 16, VectorOfFloat32 }, + { "vr18" , Vector, 16, VectorOfFloat32 }, + { "vr19" , Vector, 16, VectorOfFloat32 }, + { "vr20" , Vector, 16, VectorOfFloat32 }, + { "vr21" , Vector, 16, VectorOfFloat32 }, + { "vr22" , Vector, 16, VectorOfFloat32 }, + { "vr23" , Vector, 16, VectorOfFloat32 }, + { "vr24" , Vector, 16, VectorOfFloat32 }, + { "vr25" , Vector, 16, VectorOfFloat32 }, + { "vr26" , Vector, 16, VectorOfFloat32 }, + { "vr27" , Vector, 16, VectorOfFloat32 }, + { "vr28" , Vector, 16, VectorOfFloat32 }, + { "vr29" , Vector, 16, VectorOfFloat32 }, + { "vr30" , Vector, 16, VectorOfFloat32 }, + { "vr31" , Vector, 16, VectorOfFloat32 }, + { "vscr" , Uint, 16, Hex }, + { "vrvalid" , Uint, 4, Hex } +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo); +const size_t k_num_fpr_registers = sizeof(g_fpr_registers)/sizeof(DNBRegisterInfo); +const size_t k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +const size_t k_num_vec_registers = sizeof(g_vec_registers)/sizeof(DNBRegisterInfo); +// Total number of registers for this architecture +const size_t k_num_ppc_registers = k_num_gpr_registers + k_num_fpr_registers + k_num_exc_registers + k_num_vec_registers; + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const DNBRegisterSetInfo g_reg_sets[] = +{ + { "PowerPC Registers", NULL, k_num_ppc_registers }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpr_registers, k_num_fpr_registers }, + { "Exception State Registers", g_exc_registers, k_num_exc_registers }, + { "Altivec Registers", g_vec_registers, k_num_vec_registers } +}; +// Total number of register sets for this architecture +const size_t k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo); + + +const DNBRegisterSetInfo * +DNBArchMachPPC::GetRegisterSetInfo(nub_size_t *num_reg_sets) const +{ + *num_reg_sets = k_num_register_sets; + return g_reg_sets; +} + +bool +DNBArchMachPPC::GetRegisterValue(int set, int reg, DNBRegisterValue *value) const +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = e_regNumGPR_srr0; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = e_regNumGPR_r1; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + // Return false for now instead of returning r30 as gcc 3.x would + // use a variety of registers for the FP and it takes inspecting + // the stack to make sure there is a frame pointer before we can + // determine the FP. + return false; + + case GENERIC_REGNUM_RA: // Return Address + set = e_regSetGPR; + reg = e_regNumGPR_lr; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = e_regNumGPR_srr1; + break; + + default: + return false; + } + } + + if (!m_state.RegsAreValid(set)) + return false; + + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + value->info = *regInfo; + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + value->value.uint32 = (&m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0))[reg]; + return true; + } + break; + + case e_regSetFPR: + if (reg < 32) + { + value->value.float64 = m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpregs)[reg]; + return true; + } + else if (reg == 32) + { + value->value.uint32 = m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpscr); + return true; + } + break; + + case e_regSetEXC: + if (reg < k_num_exc_registers) + { + value->value.uint32 = (&m_state.exc.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(dar))[reg]; + return true; + } + break; + + case e_regSetVEC: + if (reg < k_num_vec_registers) + { + if (reg < 33) // FP0 - FP31 and VSCR + { + // Copy all 4 uint32 values for this vector register + value->value.v_uint32[0] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][0]; + value->value.v_uint32[1] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][1]; + value->value.v_uint32[2] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][2]; + value->value.v_uint32[3] = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg][3]; + return true; + } + else if (reg == 34) // VRVALID + { + value->value.uint32 = m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vrvalid); + return true; + } + } + break; + } + } + return false; +} + + +kern_return_t +DNBArchMachPPC::GetRegisterState(int set, bool force) +{ + switch (set) + { + case e_regSetALL: + return GetGPRState(force) | + GetFPRState(force) | + GetEXCState(force) | + GetVECState(force); + case e_regSetGPR: return GetGPRState(force); + case e_regSetFPR: return GetFPRState(force); + case e_regSetEXC: return GetEXCState(force); + case e_regSetVEC: return GetVECState(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +DNBArchMachPPC::SetRegisterState(int set) +{ + // Make sure we have a valid context to set. + kern_return_t err = GetRegisterState(set, false); + if (err != KERN_SUCCESS) + return err; + + switch (set) + { + case e_regSetALL: return SetGPRState() | SetFPRState() | SetEXCState() | SetVECState(); + case e_regSetGPR: return SetGPRState(); + case e_regSetFPR: return SetFPRState(); + case e_regSetEXC: return SetEXCState(); + case e_regSetVEC: return SetVECState(); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +bool +DNBArchMachPPC::RegisterSetStateIsValid (int set) const +{ + return m_state.RegsAreValid(set); +} + + +#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + diff --git a/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h b/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h new file mode 100644 index 000000000000..6263407460a1 --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h @@ -0,0 +1,180 @@ +//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DebugNubArchMachPPC_h__ +#define __DebugNubArchMachPPC_h__ + +#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + +#include "DNBArch.h" + +class MachThread; + +class DNBArchMachPPC : public DNBArchProtocol +{ +public: + DNBArchMachPPC(MachThread *thread) : + m_thread(thread), + m_state() + { + } + + virtual ~DNBArchMachPPC() + { + } + + virtual const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets) const; + virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *value) const; + virtual kern_return_t GetRegisterState (int set, bool force); + virtual kern_return_t SetRegisterState (int set); + virtual bool RegisterSetStateIsValid (int set) const; + + virtual uint64_t GetPC(uint64_t failValue); // Get program counter + virtual kern_return_t SetPC(uint64_t value); + virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer + virtual bool ThreadWillResume(); + virtual bool ThreadDidStop(); + + static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size); + static uint32_t GetCPUType(); + +protected: + + + kern_return_t EnableHardwareSingleStep (bool enable); + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPR, + e_regSetEXC, + e_regSetVEC, + kNumRegisterSets + } RegisterSet; + + typedef enum RegisterSetWordSizeTag + { + e_regSetWordSizeGPR = PPC_THREAD_STATE_COUNT, + e_regSetWordSizeFPR = PPC_FLOAT_STATE_COUNT, + e_regSetWordSizeEXC = PPC_EXCEPTION_STATE_COUNT, + e_regSetWordSizeVEC = PPC_VECTOR_STATE_COUNT + } RegisterSetWordSize; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + struct State + { + ppc_thread_state_t gpr; + ppc_float_state_t fpr; + ppc_exception_state_t exc; + ppc_vector_state_t vec; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpr_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + kern_return_t vec_errs[2]; // Read/Write errors + + State() + { + uint32_t i; + for (i=0; i + +#include "MacOSX/x86_64/DNBArchImplX86_64.h" +#include "DNBLog.h" +#include "MachThread.h" +#include "MachProcess.h" + +static const uint8_t g_breakpoint_opcode[] = { 0xCC }; + +const uint8_t * const +DNBArchImplX86_64::SoftwareBreakpointOpcode (nub_size_t byte_size) +{ + if (byte_size == 1) + return g_breakpoint_opcode; + return NULL; +} + +uint32_t +DNBArchImplX86_64::GetCPUType() +{ + return CPU_TYPE_X86_64; +} + +uint64_t +DNBArchImplX86_64::GetPC(uint64_t failValue) +{ + // Get program counter + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.context.gpr.__rip; + return failValue; +} + +kern_return_t +DNBArchImplX86_64::SetPC(uint64_t value) +{ + // Get program counter + kern_return_t err = GetGPRState(false); + if (err == KERN_SUCCESS) + { + m_state.context.gpr.__rip = value; + err = SetGPRState(); + } + return err == KERN_SUCCESS; +} + +uint64_t +DNBArchImplX86_64::GetSP(uint64_t failValue) +{ + // Get stack pointer + if (GetGPRState(false) == KERN_SUCCESS) + return m_state.context.gpr.__rsp; + return failValue; +} + +// Uncomment the value below to verify the values in the debugger. +//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED + +kern_return_t +DNBArchImplX86_64::GetGPRState(bool force) +{ + if (force || m_state.GetError(e_regSetGPR, Read)) + { +#if DEBUG_GPR_VALUES + m_state.context.gpr.__rax = ('a' << 8) + 'x'; + m_state.context.gpr.__rbx = ('b' << 8) + 'x'; + m_state.context.gpr.__rcx = ('c' << 8) + 'x'; + m_state.context.gpr.__rdx = ('d' << 8) + 'x'; + m_state.context.gpr.__rdi = ('d' << 8) + 'i'; + m_state.context.gpr.__rsi = ('s' << 8) + 'i'; + m_state.context.gpr.__rbp = ('b' << 8) + 'p'; + m_state.context.gpr.__rsp = ('s' << 8) + 'p'; + m_state.context.gpr.__r8 = ('r' << 8) + '8'; + m_state.context.gpr.__r9 = ('r' << 8) + '9'; + m_state.context.gpr.__r10 = ('r' << 8) + 'a'; + m_state.context.gpr.__r11 = ('r' << 8) + 'b'; + m_state.context.gpr.__r12 = ('r' << 8) + 'c'; + m_state.context.gpr.__r13 = ('r' << 8) + 'd'; + m_state.context.gpr.__r14 = ('r' << 8) + 'e'; + m_state.context.gpr.__r15 = ('r' << 8) + 'f'; + m_state.context.gpr.__rip = ('i' << 8) + 'p'; + m_state.context.gpr.__rflags = ('f' << 8) + 'l'; + m_state.context.gpr.__cs = ('c' << 8) + 's'; + m_state.context.gpr.__fs = ('f' << 8) + 's'; + m_state.context.gpr.__gs = ('g' << 8) + 's'; + m_state.SetError(e_regSetGPR, Read, 0); +#else + mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; + m_state.SetError(e_regSetGPR, Read, ::thread_get_state(m_thread->ThreadID(), x86_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, &count)); + DNBLogThreadedIf (LOG_THREAD, "::thread_get_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x" + "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx" + "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx" + "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx" + "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx" + "\n\trip = %16.16llx" + "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx", + m_thread->ThreadID(), x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT, + m_state.GetError(e_regSetGPR, Read), + m_state.context.gpr.__rax,m_state.context.gpr.__rbx,m_state.context.gpr.__rcx, + m_state.context.gpr.__rdx,m_state.context.gpr.__rdi,m_state.context.gpr.__rsi, + m_state.context.gpr.__rbp,m_state.context.gpr.__rsp,m_state.context.gpr.__r8, + m_state.context.gpr.__r9, m_state.context.gpr.__r10,m_state.context.gpr.__r11, + m_state.context.gpr.__r12,m_state.context.gpr.__r13,m_state.context.gpr.__r14, + m_state.context.gpr.__r15,m_state.context.gpr.__rip,m_state.context.gpr.__rflags, + m_state.context.gpr.__cs,m_state.context.gpr.__fs, m_state.context.gpr.__gs); + + // DNBLogThreadedIf (LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x" + // "\n\trax = %16.16llx" + // "\n\trbx = %16.16llx" + // "\n\trcx = %16.16llx" + // "\n\trdx = %16.16llx" + // "\n\trdi = %16.16llx" + // "\n\trsi = %16.16llx" + // "\n\trbp = %16.16llx" + // "\n\trsp = %16.16llx" + // "\n\t r8 = %16.16llx" + // "\n\t r9 = %16.16llx" + // "\n\tr10 = %16.16llx" + // "\n\tr11 = %16.16llx" + // "\n\tr12 = %16.16llx" + // "\n\tr13 = %16.16llx" + // "\n\tr14 = %16.16llx" + // "\n\tr15 = %16.16llx" + // "\n\trip = %16.16llx" + // "\n\tflg = %16.16llx" + // "\n\t cs = %16.16llx" + // "\n\t fs = %16.16llx" + // "\n\t gs = %16.16llx", + // m_thread->ThreadID(), + // x86_THREAD_STATE64, + // x86_THREAD_STATE64_COUNT, + // m_state.GetError(e_regSetGPR, Read), + // m_state.context.gpr.__rax, + // m_state.context.gpr.__rbx, + // m_state.context.gpr.__rcx, + // m_state.context.gpr.__rdx, + // m_state.context.gpr.__rdi, + // m_state.context.gpr.__rsi, + // m_state.context.gpr.__rbp, + // m_state.context.gpr.__rsp, + // m_state.context.gpr.__r8, + // m_state.context.gpr.__r9, + // m_state.context.gpr.__r10, + // m_state.context.gpr.__r11, + // m_state.context.gpr.__r12, + // m_state.context.gpr.__r13, + // m_state.context.gpr.__r14, + // m_state.context.gpr.__r15, + // m_state.context.gpr.__rip, + // m_state.context.gpr.__rflags, + // m_state.context.gpr.__cs, + // m_state.context.gpr.__fs, + // m_state.context.gpr.__gs); +#endif + } + return m_state.GetError(e_regSetGPR, Read); +} + +// Uncomment the value below to verify the values in the debugger. +//#define DEBUG_FPU_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED + +kern_return_t +DNBArchImplX86_64::GetFPUState(bool force) +{ + if (force || m_state.GetError(e_regSetFPU, Read)) + { +#if DEBUG_FPU_VALUES + m_state.context.fpu.__fpu_reserved[0] = -1; + m_state.context.fpu.__fpu_reserved[1] = -1; + *(uint16_t *)&(m_state.context.fpu.__fpu_fcw) = 0x1234; + *(uint16_t *)&(m_state.context.fpu.__fpu_fsw) = 0x5678; + m_state.context.fpu.__fpu_ftw = 1; + m_state.context.fpu.__fpu_rsrv1 = UINT8_MAX; + m_state.context.fpu.__fpu_fop = 2; + m_state.context.fpu.__fpu_ip = 3; + m_state.context.fpu.__fpu_cs = 4; + m_state.context.fpu.__fpu_rsrv2 = 5; + m_state.context.fpu.__fpu_dp = 6; + m_state.context.fpu.__fpu_ds = 7; + m_state.context.fpu.__fpu_rsrv3 = UINT16_MAX; + m_state.context.fpu.__fpu_mxcsr = 8; + m_state.context.fpu.__fpu_mxcsrmask = 9; + int i; + for (i=0; i<16; ++i) + { + if (i<10) + { + m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = 'a'; + m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = 'b'; + m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = 'c'; + m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = 'd'; + m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = 'e'; + m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = 'f'; + m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = 'g'; + m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = 'h'; + } + else + { + m_state.context.fpu.__fpu_stmm0.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm1.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm2.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm3.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm4.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm5.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm6.__mmst_reg[i] = INT8_MIN; + m_state.context.fpu.__fpu_stmm7.__mmst_reg[i] = INT8_MIN; + } + + m_state.context.fpu.__fpu_xmm0.__xmm_reg[i] = '0'; + m_state.context.fpu.__fpu_xmm1.__xmm_reg[i] = '1'; + m_state.context.fpu.__fpu_xmm2.__xmm_reg[i] = '2'; + m_state.context.fpu.__fpu_xmm3.__xmm_reg[i] = '3'; + m_state.context.fpu.__fpu_xmm4.__xmm_reg[i] = '4'; + m_state.context.fpu.__fpu_xmm5.__xmm_reg[i] = '5'; + m_state.context.fpu.__fpu_xmm6.__xmm_reg[i] = '6'; + m_state.context.fpu.__fpu_xmm7.__xmm_reg[i] = '7'; + m_state.context.fpu.__fpu_xmm8.__xmm_reg[i] = '8'; + m_state.context.fpu.__fpu_xmm9.__xmm_reg[i] = '9'; + m_state.context.fpu.__fpu_xmm10.__xmm_reg[i] = 'A'; + m_state.context.fpu.__fpu_xmm11.__xmm_reg[i] = 'B'; + m_state.context.fpu.__fpu_xmm12.__xmm_reg[i] = 'C'; + m_state.context.fpu.__fpu_xmm13.__xmm_reg[i] = 'D'; + m_state.context.fpu.__fpu_xmm14.__xmm_reg[i] = 'E'; + m_state.context.fpu.__fpu_xmm15.__xmm_reg[i] = 'F'; + } + for (i=0; iThreadID(), x86_FLOAT_STATE64, (thread_state_t)&m_state.context.fpu, &count)); +#endif + } + return m_state.GetError(e_regSetFPU, Read); +} + +kern_return_t +DNBArchImplX86_64::GetEXCState(bool force) +{ + if (force || m_state.GetError(e_regSetEXC, Read)) + { + mach_msg_type_number_t count = X86_EXCEPTION_STATE64_COUNT; + m_state.SetError(e_regSetEXC, Read, ::thread_get_state(m_thread->ThreadID(), x86_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, &count)); + } + return m_state.GetError(e_regSetEXC, Read); +} + +kern_return_t +DNBArchImplX86_64::SetGPRState() +{ + m_state.SetError(e_regSetGPR, Write, ::thread_set_state(m_thread->ThreadID(), x86_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, x86_THREAD_STATE64_COUNT)); + DNBLogThreadedIf (LOG_THREAD, "::thread_set_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x" + "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx" + "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx" + "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx" + "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx" + "\n\trip = %16.16llx" + "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx", + m_thread->ThreadID(), x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT, + m_state.GetError(e_regSetGPR, Write), + m_state.context.gpr.__rax,m_state.context.gpr.__rbx,m_state.context.gpr.__rcx, + m_state.context.gpr.__rdx,m_state.context.gpr.__rdi,m_state.context.gpr.__rsi, + m_state.context.gpr.__rbp,m_state.context.gpr.__rsp,m_state.context.gpr.__r8, + m_state.context.gpr.__r9, m_state.context.gpr.__r10,m_state.context.gpr.__r11, + m_state.context.gpr.__r12,m_state.context.gpr.__r13,m_state.context.gpr.__r14, + m_state.context.gpr.__r15,m_state.context.gpr.__rip,m_state.context.gpr.__rflags, + m_state.context.gpr.__cs, m_state.context.gpr.__fs, m_state.context.gpr.__gs); + return m_state.GetError(e_regSetGPR, Write); +} + +kern_return_t +DNBArchImplX86_64::SetFPUState() +{ + m_state.SetError(e_regSetFPU, Write, ::thread_set_state(m_thread->ThreadID(), x86_FLOAT_STATE64, (thread_state_t)&m_state.context.fpu, x86_FLOAT_STATE64_COUNT)); + return m_state.GetError(e_regSetFPU, Write); +} + +kern_return_t +DNBArchImplX86_64::SetEXCState() +{ + m_state.SetError(e_regSetEXC, Write, ::thread_set_state(m_thread->ThreadID(), x86_EXCEPTION_STATE64, (thread_state_t)&m_state.context.exc, X86_EXCEPTION_STATE64_COUNT)); + return m_state.GetError(e_regSetEXC, Write); +} + +void +DNBArchImplX86_64::ThreadWillResume() +{ + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread->IsStepping()) + { + // This is the primary thread, let the arch do anything it needs + EnableHardwareSingleStep(true) == KERN_SUCCESS; + } +} + +bool +DNBArchImplX86_64::ThreadDidStop() +{ + bool success = true; + + m_state.InvalidateAllRegisterStates(); + + // Are we stepping a single instruction? + if (GetGPRState(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread->IsStepping()) + { + // This was the primary thread, we need to clear the trace + // bit if so. + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + } + else + { + // The MachThread will automatically restore the suspend count + // in ThreadDidStop(), so we don't need to do anything here if + // we weren't the primary thread the last time + } + } + return success; +} + +bool +DNBArchImplX86_64::NotifyException(MachException::Data& exc) +{ + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) + { + nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS); + if (pc != INVALID_NUB_ADDRESS && pc > 0) + { + pc -= 1; + // Check for a breakpoint at one byte prior to the current PC value + // since the PC will be just past the trap. + + nub_break_t breakID = m_thread->Process()->Breakpoints().FindIDByAddress(pc); + if (NUB_BREAK_ID_IS_VALID(breakID)) + { + // Backup the PC for i386 since the trap was taken and the PC + // is at the address following the single byte trap instruction. + if (m_state.context.gpr.__rip > 0) + { + m_state.context.gpr.__rip = pc; + // Write the new PC back out + SetGPRState (); + } + + m_thread->SetCurrentBreakpoint(breakID); + } + return true; + } + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + + +// Set the single step bit in the processor status register. +kern_return_t +DNBArchImplX86_64::EnableHardwareSingleStep (bool enable) +{ + if (GetGPRState(false) == KERN_SUCCESS) + { + const uint32_t trace_bit = 0x100u; + if (enable) + m_state.context.gpr.__rflags |= trace_bit; + else + m_state.context.gpr.__rflags &= ~trace_bit; + return SetGPRState(); + } + return m_state.GetError(e_regSetGPR, Read); +} + + +//---------------------------------------------------------------------- +// Register information defintions +//---------------------------------------------------------------------- + +enum +{ + gpr_rax = 0, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + k_num_gpr_regs +}; + +enum { + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + k_num_fpu_regs, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum { + exc_trapno, + exc_err, + exc_faultvaddr, + k_num_exc_regs, +}; + + +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, + +}; + +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, +}; + +#define GPR_OFFSET(reg) (offsetof (DNBArchImplX86_64::GPR, __##reg)) +#define FPU_OFFSET(reg) (offsetof (DNBArchImplX86_64::FPU, __fpu_##reg) + offsetof (DNBArchImplX86_64::Context, fpu)) +#define EXC_OFFSET(reg) (offsetof (DNBArchImplX86_64::EXC, __##reg) + offsetof (DNBArchImplX86_64::Context, exc)) + +#define GPR_SIZE(reg) (sizeof(((DNBArchImplX86_64::GPR *)NULL)->__##reg)) +#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg)) +#define FPU_SIZE_MMST(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__mmst_reg)) +#define FPU_SIZE_XMM(reg) (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__xmm_reg)) +#define EXC_SIZE(reg) (sizeof(((DNBArchImplX86_64::EXC *)NULL)->__##reg)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that +// the register state structures are defined correctly and have the correct +// sizes and offsets. +#define DEFINE_GPR(reg) { e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), gcc_dwarf_##reg, gcc_dwarf_##reg, INVALID_NUB_REGNUM, gdb_##reg } +#define DEFINE_GPR_ALT(reg, alt, gen) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), gcc_dwarf_##reg, gcc_dwarf_##reg, gen, gdb_##reg } +#define DEFINE_GPR_ALT2(reg, alt) { e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, gdb_##reg } + +// General purpose registers for 64 bit +const DNBRegisterInfo +DNBArchImplX86_64::g_gpr_registers[] = +{ + DEFINE_GPR (rax), + DEFINE_GPR (rbx), + DEFINE_GPR (rcx), + DEFINE_GPR (rdx), + DEFINE_GPR (rdi), + DEFINE_GPR (rsi), + DEFINE_GPR_ALT (rbp, "fp", GENERIC_REGNUM_FP), + DEFINE_GPR_ALT (rsp, "sp", GENERIC_REGNUM_SP), + DEFINE_GPR (r8), + DEFINE_GPR (r9), + DEFINE_GPR (r10), + DEFINE_GPR (r11), + DEFINE_GPR (r12), + DEFINE_GPR (r13), + DEFINE_GPR (r14), + DEFINE_GPR (r15), + DEFINE_GPR_ALT (rip, "pc", GENERIC_REGNUM_PC), + DEFINE_GPR_ALT2 (rflags, "flags"), + DEFINE_GPR_ALT2 (cs, NULL), + DEFINE_GPR_ALT2 (fs, NULL), + DEFINE_GPR_ALT2 (gs, NULL), +}; + +// Floating point registers 64 bit +const DNBRegisterInfo +DNBArchImplX86_64::g_fpu_registers[] = +{ + { e_regSetFPU, fpu_fcw , "fctrl" , NULL, Uint, Hex, FPU_SIZE_UINT(fcw) , FPU_OFFSET(fcw) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_fsw , "fstat" , NULL, Uint, Hex, FPU_SIZE_UINT(fsw) , FPU_OFFSET(fsw) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_ftw , "ftag" , NULL, Uint, Hex, FPU_SIZE_UINT(ftw) , FPU_OFFSET(ftw) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_fop , "fop" , NULL, Uint, Hex, FPU_SIZE_UINT(fop) , FPU_OFFSET(fop) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_ip , "fioff" , NULL, Uint, Hex, FPU_SIZE_UINT(ip) , FPU_OFFSET(ip) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_cs , "fiseg" , NULL, Uint, Hex, FPU_SIZE_UINT(cs) , FPU_OFFSET(cs) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_dp , "fooff" , NULL, Uint, Hex, FPU_SIZE_UINT(dp) , FPU_OFFSET(dp) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_ds , "foseg" , NULL, Uint, Hex, FPU_SIZE_UINT(ds) , FPU_OFFSET(ds) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_mxcsr , "mxcsr" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr) , FPU_OFFSET(mxcsr) , -1, -1, -1, -1 }, + { e_regSetFPU, fpu_mxcsrmask, "mxcsrmask" , NULL, Uint, Hex, FPU_SIZE_UINT(mxcsrmask) , FPU_OFFSET(mxcsrmask) , -1, -1, -1, -1 }, + + { e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), gcc_dwarf_stmm0, gcc_dwarf_stmm0, -1, gdb_stmm0 }, + { e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), gcc_dwarf_stmm1, gcc_dwarf_stmm1, -1, gdb_stmm1 }, + { e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), gcc_dwarf_stmm2, gcc_dwarf_stmm2, -1, gdb_stmm2 }, + { e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), gcc_dwarf_stmm3, gcc_dwarf_stmm3, -1, gdb_stmm3 }, + { e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), gcc_dwarf_stmm4, gcc_dwarf_stmm4, -1, gdb_stmm4 }, + { e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), gcc_dwarf_stmm5, gcc_dwarf_stmm5, -1, gdb_stmm5 }, + { e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), gcc_dwarf_stmm6, gcc_dwarf_stmm6, -1, gdb_stmm6 }, + { e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8, FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), gcc_dwarf_stmm7, gcc_dwarf_stmm7, -1, gdb_stmm7 }, + + { e_regSetFPU, fpu_xmm0 , "xmm0" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm0) , FPU_OFFSET(xmm0) , gcc_dwarf_xmm0 , gcc_dwarf_xmm0 , -1, gdb_xmm0 }, + { e_regSetFPU, fpu_xmm1 , "xmm1" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm1) , FPU_OFFSET(xmm1) , gcc_dwarf_xmm1 , gcc_dwarf_xmm1 , -1, gdb_xmm1 }, + { e_regSetFPU, fpu_xmm2 , "xmm2" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm2) , FPU_OFFSET(xmm2) , gcc_dwarf_xmm2 , gcc_dwarf_xmm2 , -1, gdb_xmm2 }, + { e_regSetFPU, fpu_xmm3 , "xmm3" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm3) , FPU_OFFSET(xmm3) , gcc_dwarf_xmm3 , gcc_dwarf_xmm3 , -1, gdb_xmm3 }, + { e_regSetFPU, fpu_xmm4 , "xmm4" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm4) , FPU_OFFSET(xmm4) , gcc_dwarf_xmm4 , gcc_dwarf_xmm4 , -1, gdb_xmm4 }, + { e_regSetFPU, fpu_xmm5 , "xmm5" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm5) , FPU_OFFSET(xmm5) , gcc_dwarf_xmm5 , gcc_dwarf_xmm5 , -1, gdb_xmm5 }, + { e_regSetFPU, fpu_xmm6 , "xmm6" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm6) , FPU_OFFSET(xmm6) , gcc_dwarf_xmm6 , gcc_dwarf_xmm6 , -1, gdb_xmm6 }, + { e_regSetFPU, fpu_xmm7 , "xmm7" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm7) , FPU_OFFSET(xmm7) , gcc_dwarf_xmm7 , gcc_dwarf_xmm7 , -1, gdb_xmm7 }, + { e_regSetFPU, fpu_xmm8 , "xmm8" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm8) , FPU_OFFSET(xmm8) , gcc_dwarf_xmm8 , gcc_dwarf_xmm8 , -1, gdb_xmm8 }, + { e_regSetFPU, fpu_xmm9 , "xmm9" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm9) , FPU_OFFSET(xmm9) , gcc_dwarf_xmm9 , gcc_dwarf_xmm9 , -1, gdb_xmm9 }, + { e_regSetFPU, fpu_xmm10, "xmm10" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm10) , FPU_OFFSET(xmm10), gcc_dwarf_xmm10, gcc_dwarf_xmm10, -1, gdb_xmm10 }, + { e_regSetFPU, fpu_xmm11, "xmm11" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm11) , FPU_OFFSET(xmm11), gcc_dwarf_xmm11, gcc_dwarf_xmm11, -1, gdb_xmm11 }, + { e_regSetFPU, fpu_xmm12, "xmm12" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm12) , FPU_OFFSET(xmm12), gcc_dwarf_xmm12, gcc_dwarf_xmm12, -1, gdb_xmm12 }, + { e_regSetFPU, fpu_xmm13, "xmm13" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm13) , FPU_OFFSET(xmm13), gcc_dwarf_xmm13, gcc_dwarf_xmm13, -1, gdb_xmm13 }, + { e_regSetFPU, fpu_xmm14, "xmm14" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm14) , FPU_OFFSET(xmm14), gcc_dwarf_xmm14, gcc_dwarf_xmm14, -1, gdb_xmm14 }, + { e_regSetFPU, fpu_xmm15, "xmm15" , NULL, Vector, VectorOfUInt8, FPU_SIZE_XMM(xmm15) , FPU_OFFSET(xmm15), gcc_dwarf_xmm15, gcc_dwarf_xmm15, -1, gdb_xmm15 }, +}; + +// Exception registers + +const DNBRegisterInfo +DNBArchImplX86_64::g_exc_registers[] = +{ + { e_regSetEXC, exc_trapno, "trapno" , NULL, Uint, Hex, EXC_SIZE (trapno) , EXC_OFFSET (trapno) , -1, -1, -1, -1 }, + { e_regSetEXC, exc_err, "err" , NULL, Uint, Hex, EXC_SIZE (err) , EXC_OFFSET (err) , -1, -1, -1, -1 }, + { e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex, EXC_SIZE (faultvaddr), EXC_OFFSET (faultvaddr) , -1, -1, -1, -1 } +}; + +// Number of registers in each register set +const size_t DNBArchImplX86_64::k_num_gpr_registers = sizeof(g_gpr_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplX86_64::k_num_fpu_registers = sizeof(g_fpu_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplX86_64::k_num_exc_registers = sizeof(g_exc_registers)/sizeof(DNBRegisterInfo); +const size_t DNBArchImplX86_64::k_num_all_registers = k_num_gpr_registers + k_num_fpu_registers + k_num_exc_registers; + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +const DNBRegisterSetInfo +DNBArchImplX86_64::g_reg_sets[] = +{ + { "x86_64 Registers", NULL, k_num_all_registers }, + { "General Purpose Registers", g_gpr_registers, k_num_gpr_registers }, + { "Floating Point Registers", g_fpu_registers, k_num_fpu_registers }, + { "Exception State Registers", g_exc_registers, k_num_exc_registers } +}; +// Total number of register sets for this architecture +const size_t DNBArchImplX86_64::k_num_register_sets = sizeof(g_reg_sets)/sizeof(DNBRegisterSetInfo); + + +const DNBRegisterSetInfo * +DNBArchImplX86_64::GetRegisterSetInfo (nub_size_t *num_reg_sets) +{ + *num_reg_sets = k_num_register_sets; + return g_reg_sets; +} + +bool +DNBArchImplX86_64::GetRegisterValue(int set, int reg, DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_rip; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_rsp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_rbp; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_rflags; + break; + + case GENERIC_REGNUM_RA: // Return Address + default: + return false; + } + } + + if (GetRegisterState(set, false) != KERN_SUCCESS) + return false; + + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + value->info = *regInfo; + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + value->value.uint64 = ((uint64_t*)(&m_state.context.gpr))[reg]; + return true; + } + break; + + case e_regSetFPU: + switch (reg) + { + case fpu_fcw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)); return true; + case fpu_fsw: value->value.uint16 = *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)); return true; + case fpu_ftw: value->value.uint8 = m_state.context.fpu.__fpu_ftw; return true; + case fpu_fop: value->value.uint16 = m_state.context.fpu.__fpu_fop; return true; + case fpu_ip: value->value.uint32 = m_state.context.fpu.__fpu_ip; return true; + case fpu_cs: value->value.uint16 = m_state.context.fpu.__fpu_cs; return true; + case fpu_dp: value->value.uint32 = m_state.context.fpu.__fpu_dp; return true; + case fpu_ds: value->value.uint16 = m_state.context.fpu.__fpu_ds; return true; + case fpu_mxcsr: value->value.uint32 = m_state.context.fpu.__fpu_mxcsr; return true; + case fpu_mxcsrmask: value->value.uint32 = m_state.context.fpu.__fpu_mxcsrmask; return true; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + memcpy(&value->value.uint8, &m_state.context.fpu.__fpu_stmm0 + (reg - fpu_stmm0), 10); + return true; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + memcpy(&value->value.uint8, &m_state.context.fpu.__fpu_xmm0 + (reg - fpu_xmm0), 16); + return true; + } + break; + + case e_regSetEXC: + switch (reg) + { + case exc_trapno: value->value.uint32 = m_state.context.exc.__trapno; return true; + case exc_err: value->value.uint32 = m_state.context.exc.__err; return true; + case exc_faultvaddr:value->value.uint64 = m_state.context.exc.__faultvaddr; return true; + } + break; + } + } + return false; +} + + +bool +DNBArchImplX86_64::SetRegisterValue(int set, int reg, const DNBRegisterValue *value) +{ + if (set == REGISTER_SET_GENERIC) + { + switch (reg) + { + case GENERIC_REGNUM_PC: // Program Counter + set = e_regSetGPR; + reg = gpr_rip; + break; + + case GENERIC_REGNUM_SP: // Stack Pointer + set = e_regSetGPR; + reg = gpr_rsp; + break; + + case GENERIC_REGNUM_FP: // Frame Pointer + set = e_regSetGPR; + reg = gpr_rbp; + break; + + case GENERIC_REGNUM_FLAGS: // Processor flags register + set = e_regSetGPR; + reg = gpr_rflags; + break; + + case GENERIC_REGNUM_RA: // Return Address + default: + return false; + } + } + + if (GetRegisterState(set, false) != KERN_SUCCESS) + return false; + + bool success = false; + const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg); + if (regInfo) + { + switch (set) + { + case e_regSetGPR: + if (reg < k_num_gpr_registers) + { + ((uint64_t*)(&m_state.context.gpr))[reg] = value->value.uint64; + success = true; + } + break; + + case e_regSetFPU: + switch (reg) + { + case fpu_fcw: *((uint16_t *)(&m_state.context.fpu.__fpu_fcw)) = value->value.uint16; success = true; break; + case fpu_fsw: *((uint16_t *)(&m_state.context.fpu.__fpu_fsw)) = value->value.uint16; success = true; break; + case fpu_ftw: m_state.context.fpu.__fpu_ftw = value->value.uint8; success = true; break; + case fpu_fop: m_state.context.fpu.__fpu_fop = value->value.uint16; success = true; break; + case fpu_ip: m_state.context.fpu.__fpu_ip = value->value.uint32; success = true; break; + case fpu_cs: m_state.context.fpu.__fpu_cs = value->value.uint16; success = true; break; + case fpu_dp: m_state.context.fpu.__fpu_dp = value->value.uint32; success = true; break; + case fpu_ds: m_state.context.fpu.__fpu_ds = value->value.uint16; success = true; break; + case fpu_mxcsr: m_state.context.fpu.__fpu_mxcsr = value->value.uint32; success = true; break; + case fpu_mxcsrmask: m_state.context.fpu.__fpu_mxcsrmask = value->value.uint32; success = true; break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + memcpy (&m_state.context.fpu.__fpu_stmm0 + (reg - fpu_stmm0), &value->value.uint8, 10); + success = true; + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + memcpy (&m_state.context.fpu.__fpu_xmm0 + (reg - fpu_xmm0), &value->value.uint8, 16); + success = true; + break; + } + break; + + case e_regSetEXC: + switch (reg) + { + case exc_trapno: m_state.context.exc.__trapno = value->value.uint32; success = true; break; + case exc_err: m_state.context.exc.__err = value->value.uint32; success = true; break; + case exc_faultvaddr:m_state.context.exc.__faultvaddr = value->value.uint64; success = true; break; + } + break; + } + } + + if (success) + return SetRegisterState(set) == KERN_SUCCESS; + return false; +} + + +nub_size_t +DNBArchImplX86_64::GetRegisterContext (void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context); + + if (buf && buf_len) + { + if (size > buf_len) + size = buf_len; + + bool force = false; + if (GetGPRState(force) | GetFPUState(force) | GetEXCState(force)) + return 0; + ::memcpy (buf, &m_state.context, size); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size); + // Return the size of the register context even if NULL was passed in + return size; +} + +nub_size_t +DNBArchImplX86_64::SetRegisterContext (const void *buf, nub_size_t buf_len) +{ + nub_size_t size = sizeof (m_state.context); + if (buf == NULL || buf_len == 0) + size = 0; + + if (size) + { + if (size > buf_len) + size = buf_len; + + ::memcpy (&m_state.context, buf, size); + SetGPRState(); + SetFPUState(); + SetEXCState(); + } + DNBLogThreadedIf (LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %zu) => %zu", buf, buf_len, size); + return size; +} + + +kern_return_t +DNBArchImplX86_64::GetRegisterState(int set, bool force) +{ + switch (set) + { + case e_regSetALL: return GetGPRState(force) | GetFPUState(force) | GetEXCState(force); + case e_regSetGPR: return GetGPRState(force); + case e_regSetFPU: return GetFPUState(force); + case e_regSetEXC: return GetEXCState(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +DNBArchImplX86_64::SetRegisterState(int set) +{ + // Make sure we have a valid context to set. + if (RegisterSetStateIsValid(set)) + { + switch (set) + { + case e_regSetALL: return SetGPRState() | SetFPUState() | SetEXCState(); + case e_regSetGPR: return SetGPRState(); + case e_regSetFPU: return SetFPUState(); + case e_regSetEXC: return SetEXCState(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +bool +DNBArchImplX86_64::RegisterSetStateIsValid (int set) const +{ + return m_state.RegsAreValid(set); +} + + + +#endif // #if defined (__i386__) diff --git a/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h new file mode 100644 index 000000000000..f445d473892e --- /dev/null +++ b/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h @@ -0,0 +1,199 @@ +//===-- DNBArchImplX86_64.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/25/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __DNBArchImplX86_64_h__ +#define __DNBArchImplX86_64_h__ + +//#if defined (__i386__) +#if defined(__x86_64__) +#include "DNBArch.h" +#include +#include + + +class MachThread; + +class DNBArchImplX86_64 : public DNBArchProtocol +{ +public: + DNBArchImplX86_64(MachThread *thread) : + m_thread(thread), + m_state() + { + } + virtual ~DNBArchImplX86_64() + { + } + + static const DNBRegisterSetInfo * + GetRegisterSetInfo(nub_size_t *num_reg_sets); + + virtual bool GetRegisterValue(int set, int reg, DNBRegisterValue *reg); + virtual bool SetRegisterValue(int set, int reg, const DNBRegisterValue *reg); + virtual nub_size_t GetRegisterContext (void *buf, nub_size_t buf_len); + virtual nub_size_t SetRegisterContext (const void *buf, nub_size_t buf_len); + + virtual kern_return_t GetRegisterState (int set, bool force); + virtual kern_return_t SetRegisterState (int set); + virtual bool RegisterSetStateIsValid (int set) const; + + virtual uint64_t GetPC(uint64_t failValue); // Get program counter + virtual kern_return_t SetPC(uint64_t value); + virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer + virtual void ThreadWillResume(); + virtual bool ThreadDidStop(); + virtual bool NotifyException(MachException::Data& exc); + + static const uint8_t * const SoftwareBreakpointOpcode (nub_size_t byte_size); + static uint32_t GetCPUType(); + +protected: + kern_return_t EnableHardwareSingleStep (bool enable); + + typedef x86_thread_state64_t GPR; + typedef x86_float_state64_t FPU; + typedef x86_exception_state64_t EXC; + + static const DNBRegisterInfo g_gpr_registers[]; + static const DNBRegisterInfo g_fpu_registers[]; + static const DNBRegisterInfo g_exc_registers[]; + static const DNBRegisterSetInfo g_reg_sets[]; + static const size_t k_num_gpr_registers; + static const size_t k_num_fpu_registers; + static const size_t k_num_exc_registers; + static const size_t k_num_all_registers; + static const size_t k_num_register_sets; + + typedef enum RegisterSetTag + { + e_regSetALL = REGISTER_SET_ALL, + e_regSetGPR, + e_regSetFPU, + e_regSetEXC, + kNumRegisterSets + } RegisterSet; + + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + struct Context + { + GPR gpr; + FPU fpu; + EXC exc; + }; + + struct State + { + Context context; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + + State() + { + uint32_t i; + for (i=0; i + +class PThreadCondition +{ +public: + + PThreadCondition() + { + ::pthread_cond_init (&m_condition, NULL); + } + + ~PThreadCondition() + { + ::pthread_cond_destroy (&m_condition); + } + + pthread_cond_t *Condition() + { + return &m_condition; + } + + int Broadcast() + { + return ::pthread_cond_broadcast (&m_condition); + } + + int Signal() + { + return ::pthread_cond_signal (&m_condition); + } + +protected: + pthread_cond_t m_condition; +}; + +#endif + diff --git a/lldb/tools/debugserver/source/PThreadEvent.cpp b/lldb/tools/debugserver/source/PThreadEvent.cpp new file mode 100644 index 000000000000..b087bfc7d481 --- /dev/null +++ b/lldb/tools/debugserver/source/PThreadEvent.cpp @@ -0,0 +1,227 @@ +//===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/16/07. +// +//===----------------------------------------------------------------------===// + +#include "PThreadEvent.h" +#include "errno.h" +#include "DNBLog.h" + +PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits) : + m_mutex(), + m_set_condition(), + m_reset_condition(), + m_bits(bits), + m_validBits(validBits), + m_reset_ack_mask(0) +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)", this, __FUNCTION__, bits, validBits); +} + +PThreadEvent::~PThreadEvent() +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); +} + + +uint32_t +PThreadEvent::NewEventBit() +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + uint32_t mask = 1; + while (mask & m_validBits) + mask <<= 1; + m_validBits |= mask; + return mask; +} + +void +PThreadEvent::FreeEventBits(const uint32_t mask) +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask); + if (mask) + { + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + m_bits &= ~mask; + m_validBits &= ~mask; + } +} + + +uint32_t +PThreadEvent::GetEventBits() const +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + uint32_t bits = m_bits; + return bits; +} + +// Replace the event bits with a new bitmask value +void +PThreadEvent::ReplaceEventBits(const uint32_t bits) +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, bits); + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + // Make sure we have some bits and that they aren't already set... + if (m_bits != bits) + { + // Figure out which bits are changing + uint32_t changed_bits = m_bits ^ bits; + // Set the new bit values + m_bits = bits; + // If any new bits are set, then broadcast + if (changed_bits & m_bits) + m_set_condition.Broadcast(); + } +} + +// Set one or more event bits and broadcast if any new event bits get set +// that weren't already set. + +void +PThreadEvent::SetEvents(const uint32_t mask) +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask); + // Make sure we have some bits to set + if (mask) + { + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + // Save the old event bit state so we can tell if things change + uint32_t old = m_bits; + // Set the all event bits that are set in 'mask' + m_bits |= mask; + // Broadcast only if any extra bits got set. + if (old != m_bits) + m_set_condition.Broadcast(); + } +} + +// Reset one or more event bits +void +PThreadEvent::ResetEvents(const uint32_t mask) +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask); + if (mask) + { + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + + // Save the old event bit state so we can tell if things change + uint32_t old = m_bits; + // Clear the all event bits that are set in 'mask' + m_bits &= ~mask; + // Broadcast only if any extra bits got reset. + if (old != m_bits) + m_reset_condition.Broadcast(); + } +} + +//---------------------------------------------------------------------- +// Wait until 'timeout_abstime' for any events that are set in +// 'mask'. If 'timeout_abstime' is NULL, then wait forever. +//---------------------------------------------------------------------- +uint32_t +PThreadEvent::WaitForSetEvents(const uint32_t mask, const struct timespec *timeout_abstime) const +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime); + 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 (PThreadMutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + do + { + // Check our predicate (event bits) in case any are already set + if (mask & m_bits) + { + uint32_t bits_set = mask & m_bits; + // Our PThreadMutex::Locker will automatically unlock our mutex + return bits_set; + } + if (timeout_abstime) + { + // Wait for condition to get broadcast, or for a timeout. If we get + // a timeout we will drop out of the do loop and return false which + // is what we want. + err = ::pthread_cond_timedwait (m_set_condition.Condition(), m_mutex.Mutex(), timeout_abstime); + // Retest our predicate in case of a race condition right at the end + // of the timeout. + if (err == ETIMEDOUT) + { + uint32_t bits_set = mask & m_bits; + return bits_set; + } + } + else + { + // Wait for condition to get broadcast. The only error this function + // should return is if + err = ::pthread_cond_wait (m_set_condition.Condition(), m_mutex.Mutex()); + } + } while (err == 0); + return 0; +} + +//---------------------------------------------------------------------- +// Wait until 'timeout_abstime' for any events in 'mask' to reset. +// If 'timeout_abstime' is NULL, then wait forever. +//---------------------------------------------------------------------- +uint32_t +PThreadEvent::WaitForEventsToReset(const uint32_t mask, const struct timespec *timeout_abstime) const +{ + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime); + 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 (PThreadMutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + PTHREAD_MUTEX_LOCKER (locker, m_mutex); + do + { + // Check our predicate (event bits) each time through this do loop + if ((mask & m_bits) == 0) + { + // All the bits requested have been reset, return zero indicating + // which bits from the mask were still set (none of them) + return 0; + } + if (timeout_abstime) + { + // Wait for condition to get broadcast, or for a timeout. If we get + // a timeout we will drop out of the do loop and return false which + // is what we want. + err = ::pthread_cond_timedwait (m_reset_condition.Condition(), m_mutex.Mutex(), timeout_abstime); + } + else + { + // Wait for condition to get broadcast. The only error this function + // should return is if + err = ::pthread_cond_wait (m_reset_condition.Condition(), m_mutex.Mutex()); + } + } while (err == 0); + // Return a mask indicating which bits (if any) were still set + return mask & m_bits; +} + +uint32_t +PThreadEvent::WaitForResetAck (const uint32_t mask, const struct timespec *timeout_abstime) const +{ + if (mask & m_reset_ack_mask) + { + // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime); + return WaitForEventsToReset (mask & m_reset_ack_mask, timeout_abstime); + } + return 0; +} diff --git a/lldb/tools/debugserver/source/PThreadEvent.h b/lldb/tools/debugserver/source/PThreadEvent.h new file mode 100644 index 000000000000..7928566e8b16 --- /dev/null +++ b/lldb/tools/debugserver/source/PThreadEvent.h @@ -0,0 +1,59 @@ +//===-- PThreadEvent.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/16/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __PThreadEvent_h__ +#define __PThreadEvent_h__ +#include "PThreadMutex.h" +#include "PThreadCondition.h" +#include +#include + +class PThreadEvent +{ +public: + PThreadEvent (uint32_t bits = 0, uint32_t validBits = 0); + ~PThreadEvent (); + + uint32_t NewEventBit (); + void FreeEventBits (const uint32_t mask); + + void ReplaceEventBits (const uint32_t bits); + uint32_t GetEventBits () const; + void SetEvents (const uint32_t mask); + void ResetEvents (const uint32_t mask); + // Wait for events to be set or reset. These functions take an optional + // timeout value. If timeout is NULL an infinite timeout will be used. + uint32_t WaitForSetEvents (const uint32_t mask, const struct timespec *timeout_abstime = NULL) const; + uint32_t WaitForEventsToReset(const uint32_t mask, const struct timespec *timeout_abstime = NULL) const; + + uint32_t GetResetAckMask () const { return m_reset_ack_mask; } + uint32_t SetResetAckMask (uint32_t mask) { return m_reset_ack_mask = mask; } + uint32_t WaitForResetAck (const uint32_t mask, const struct timespec *timeout_abstime = NULL) const; +protected: + //---------------------------------------------------------------------- + // pthread condition and mutex variable to controll access and allow + // blocking between the main thread and the spotlight index thread. + //---------------------------------------------------------------------- + mutable PThreadMutex m_mutex; + mutable PThreadCondition m_set_condition; + mutable PThreadCondition m_reset_condition; + uint32_t m_bits; + uint32_t m_validBits; + uint32_t m_reset_ack_mask; +private: + PThreadEvent(const PThreadEvent&); // Outlaw copy contructor + PThreadEvent& operator=(const PThreadEvent& rhs); + +}; + +#endif // #ifndef __PThreadEvent_h__ diff --git a/lldb/tools/debugserver/source/PThreadMutex.cpp b/lldb/tools/debugserver/source/PThreadMutex.cpp new file mode 100644 index 000000000000..bd91ed0154b1 --- /dev/null +++ b/lldb/tools/debugserver/source/PThreadMutex.cpp @@ -0,0 +1,84 @@ +//===-- PThreadMutex.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/9/08. +// +//===----------------------------------------------------------------------===// + +#include "PThreadMutex.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "DNBTimer.h" + +#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS) + +PThreadMutex::Locker::Locker(PThreadMutex& m, const char *function, const char *file, const int line) : + m_pMutex(m.Mutex()), + m_function(function), + m_file(file), + m_line(line), + m_lock_time(0) +{ + Lock(); +} + +PThreadMutex::Locker::Locker(PThreadMutex* m, const char *function, const char *file, const int line) : + m_pMutex(m ? m->Mutex() : NULL), + m_function(function), + m_file(file), + m_line(line), + m_lock_time(0) +{ + Lock(); +} + +PThreadMutex::Locker::Locker(pthread_mutex_t *mutex, const char *function, const char *file, const int line) : + m_pMutex(mutex), + m_function(function), + m_file(file), + m_line(line), + m_lock_time(0) +{ + Lock(); +} + + +PThreadMutex::Locker::~Locker() +{ + Unlock(); +} + + +void +PThreadMutex::Locker::Lock() +{ + if (m_pMutex) + { + m_lock_time = DNBTimer::GetTimeOfDay(); + if (::pthread_mutex_trylock (m_pMutex) != 0) + { + fprintf(stdout, "::pthread_mutex_trylock (%8.8p) mutex is locked (function %s in %s:%i), waiting...\n", m_pMutex, m_function, m_file, m_line); + ::pthread_mutex_lock (m_pMutex); + fprintf(stdout, "::pthread_mutex_lock (%8.8p) succeeded after %6llu usecs (function %s in %s:%i)\n", m_pMutex, DNBTimer::GetTimeOfDay() - m_lock_time, m_function, m_file, m_line); + } + } +} + + +void +PThreadMutex::Locker::Unlock() +{ + fprintf(stdout, "::pthread_mutex_unlock (%8.8p) had lock for %6llu usecs in %s in %s:%i\n", m_pMutex, DNBTimer::GetTimeOfDay() - m_lock_time, m_function, m_file, m_line); + ::pthread_mutex_unlock (m_pMutex); +} + +#endif diff --git a/lldb/tools/debugserver/source/PThreadMutex.h b/lldb/tools/debugserver/source/PThreadMutex.h new file mode 100644 index 000000000000..9a12f6e8e034 --- /dev/null +++ b/lldb/tools/debugserver/source/PThreadMutex.h @@ -0,0 +1,148 @@ +//===-- PThreadMutex.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/16/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __PThreadMutex_h__ +#define __PThreadMutex_h__ + +#include +#include +#include + +//#define DEBUG_PTHREAD_MUTEX_DEADLOCKS 1 + +#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS) +#define PTHREAD_MUTEX_LOCKER(var, mutex) PThreadMutex::Locker var(mutex, __FUNCTION__, __FILE__, __LINE__) + +#else +#define PTHREAD_MUTEX_LOCKER(var, mutex) PThreadMutex::Locker var(mutex) +#endif + +class PThreadMutex +{ +public: + + class Locker + { + public: +#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS) + + Locker(PThreadMutex& m, const char *function, const char *file, int line); + Locker(PThreadMutex* m, const char *function, const char *file, int line); + Locker(pthread_mutex_t *mutex, const char *function, const char *file, int line); + ~Locker(); + void Lock(); + void Unlock(); + +#else + Locker(PThreadMutex& m) : + m_pMutex(m.Mutex()) + { + Lock(); + } + + Locker(PThreadMutex* m) : + m_pMutex(m ? m->Mutex() : NULL) + { + Lock(); + } + + Locker(pthread_mutex_t *mutex) : + m_pMutex(mutex) + { + Lock(); + } + + void Lock() + { + if (m_pMutex) + ::pthread_mutex_lock (m_pMutex); + } + + void Unlock() + { + if (m_pMutex) + ::pthread_mutex_unlock (m_pMutex); + } + + ~Locker() + { + Unlock(); + } + +#endif + + // unlock any the current mutex and lock the new one if it is valid + void Reset(pthread_mutex_t *pMutex = NULL) + { + Unlock(); + m_pMutex = pMutex; + Lock(); + } + pthread_mutex_t *m_pMutex; +#if defined (DEBUG_PTHREAD_MUTEX_DEADLOCKS) + const char *m_function; + const char *m_file; + int m_line; + uint64_t m_lock_time; +#endif + }; + + + PThreadMutex() + { + int err; + err = ::pthread_mutex_init (&m_mutex, NULL); assert(err == 0); + } + + PThreadMutex(int type) + { + int err; + ::pthread_mutexattr_t attr; + err = ::pthread_mutexattr_init (&attr); assert(err == 0); + err = ::pthread_mutexattr_settype (&attr, type); assert(err == 0); + err = ::pthread_mutex_init (&m_mutex, &attr); assert(err == 0); + err = ::pthread_mutexattr_destroy (&attr); assert(err == 0); + } + + ~PThreadMutex() + { + int err; + err = ::pthread_mutex_destroy (&m_mutex); + if (err != 0) + { + err = Unlock(); + if (err == 0) + ::pthread_mutex_destroy (&m_mutex); + } + } + + pthread_mutex_t *Mutex() + { + return &m_mutex; + } + + int Lock() + { + return ::pthread_mutex_lock (&m_mutex); + } + + int Unlock() + { + return ::pthread_mutex_unlock (&m_mutex); + } + +protected: + pthread_mutex_t m_mutex; +}; + +#endif diff --git a/lldb/tools/debugserver/source/ProfileObjectiveC.cpp b/lldb/tools/debugserver/source/ProfileObjectiveC.cpp new file mode 100644 index 000000000000..1fd2d526d250 --- /dev/null +++ b/lldb/tools/debugserver/source/ProfileObjectiveC.cpp @@ -0,0 +1,393 @@ +//===-- ProfileObjectiveC.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 10/4/07. +// +//===----------------------------------------------------------------------===// + +#include "ProfileObjectiveC.h" +#include "DNB.h" +#include +#include + +#if defined (__powerpc__) || defined (__ppc__) +#define OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR ((nub_addr_t)0xfffeff00) +#endif + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +ProfileObjectiveC::ProfileObjectiveC() : + m_pid(INVALID_NUB_PROCESS), + m_objcStats(), + m_hit_count(0), + m_dump_count(0xffff) +{ + memset(&m_begin_time, 0, sizeof(m_begin_time)); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProfileObjectiveC::~ProfileObjectiveC() +{ +} + +//---------------------------------------------------------------------- +// Clear any counts that we may have had +//---------------------------------------------------------------------- +void +ProfileObjectiveC::Clear() +{ + if (m_pid != INVALID_NUB_PROCESS) + { + DNBBreakpointClear(m_pid, m_objc_msgSend.breakID); + DNBBreakpointClear(m_pid, m_objc_msgSendSuper.breakID); +#if defined (__powerpc__) || defined (__ppc__) + DNBBreakpointClear(m_pid, m_objc_msgSend_rtp.breakID); +#endif + } + m_objc_msgSend.Clear(); + m_objc_msgSendSuper.Clear(); +#if defined (__powerpc__) || defined (__ppc__) + memset(m_objc_msgSend_opcode, 0, k_opcode_size); + m_objc_msgSend_rtp.Clear(); +#endif + memset(&m_begin_time, 0, sizeof(m_begin_time)); + m_objcStats.clear(); +} + +void +ProfileObjectiveC::Initialize(nub_process_t pid) +{ + Clear(); + m_pid = pid; +} + + +void +ProfileObjectiveC::ProcessStateChanged(nub_state_t state) +{ + //printf("ProfileObjectiveC::%s(%s)\n", __FUNCTION__, DNBStateAsString(state)); + switch (state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(); + break; + + case eStateStopped: +#if defined (__powerpc__) || defined (__ppc__) + if (NUB_BREAK_ID_IS_VALID(m_objc_msgSend.breakID) && !NUB_BREAK_ID_IS_VALID(m_objc_msgSend_rtp.breakID)) + { + nub_thread_t tid = DNBProcessGetCurrentThread(m_pid); + DNBRegisterValue pc_value; + if (DNBThreadGetRegisterValueByName(m_pid, tid, REGISTER_SET_ALL, "srr0" , &pc_value)) + { + nub_addr_t pc = pc_value.value.uint32; + if (pc == OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR) + { + // Restore previous first instruction to 0xfffeff00 in comm page + DNBProcessMemoryWrite(m_pid, OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR, k_opcode_size, m_objc_msgSend_opcode); + //printf("Setting breakpoint on _objc_msgSend_rtp...\n"); + m_objc_msgSend_rtp.breakID = DNBBreakpointSet(m_pid, OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR); + if (NUB_BREAK_ID_IS_VALID(m_objc_msgSend_rtp.breakID)) + { + DNBBreakpointSetCallback(m_pid, m_objc_msgSend_rtp.breakID, ProfileObjectiveC::MessageSendBreakpointCallback, this); + } + } + } + } +#endif + DumpStats(m_pid, stdout); + break; + + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} + +void +ProfileObjectiveC::SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos) +{ + //printf("ProfileObjectiveC::%s(%p, %u)\n", __FUNCTION__, image_infos, num_image_infos); + if (m_objc_msgSend.IsValid() && m_objc_msgSendSuper.IsValid()) + return; + + if (image_infos) + { + nub_process_t pid = m_pid; + nub_size_t i; + for (i = 0; i < num_image_infos; i++) + { + if (strcmp(image_infos[i].name, "/usr/lib/libobjc.A.dylib") == 0) + { + if (!NUB_BREAK_ID_IS_VALID(m_objc_msgSend.breakID)) + { + m_objc_msgSend.addr = DNBProcessLookupAddress(pid, "_objc_msgSend", image_infos[i].name); + + if (m_objc_msgSend.addr != INVALID_NUB_ADDRESS) + { +#if defined (__powerpc__) || defined (__ppc__) + if (DNBProcessMemoryRead(pid, m_objc_msgSend.addr, k_opcode_size, m_objc_msgSend_opcode) != k_opcode_size) + memset(m_objc_msgSend_opcode, 0, sizeof(m_objc_msgSend_opcode)); +#endif + m_objc_msgSend.breakID = DNBBreakpointSet(pid, m_objc_msgSend.addr, 4, false); + if (NUB_BREAK_ID_IS_VALID(m_objc_msgSend.breakID)) + DNBBreakpointSetCallback(pid, m_objc_msgSend.breakID, ProfileObjectiveC::MessageSendBreakpointCallback, this); + } + } + + if (!NUB_BREAK_ID_IS_VALID(m_objc_msgSendSuper.breakID)) + { + m_objc_msgSendSuper.addr = DNBProcessLookupAddress(pid, "_objc_msgSendSuper", image_infos[i].name); + + if (m_objc_msgSendSuper.addr != INVALID_NUB_ADDRESS) + { + m_objc_msgSendSuper.breakID = DNBBreakpointSet(pid, m_objc_msgSendSuper.addr, 4, false); + if (NUB_BREAK_ID_IS_VALID(m_objc_msgSendSuper.breakID)) + DNBBreakpointSetCallback(pid, m_objc_msgSendSuper.breakID, ProfileObjectiveC::MessageSendSuperBreakpointCallback, this); + } + } + break; + } + } + } +} + + +void +ProfileObjectiveC::SetStartTime() +{ + gettimeofday(&m_begin_time, NULL); +} + +void +ProfileObjectiveC::SelectorHit(objc_class_ptr_t isa, objc_selector_t sel) +{ + m_objcStats[isa][sel]++; +} + +nub_bool_t +ProfileObjectiveC::MessageSendBreakpointCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *userData) +{ + ProfileObjectiveC *profile_objc = (ProfileObjectiveC*)userData; + uint32_t hit_count = profile_objc->IncrementHitCount(); + if (hit_count == 1) + profile_objc->SetStartTime(); + + objc_class_ptr_t objc_self = 0; + objc_selector_t objc_selector = 0; +#if defined (__i386__) + DNBRegisterValue esp; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "esp", &esp)) + { + uint32_t uval32[2]; + if (DNBProcessMemoryRead(pid, esp.value.uint32 + 4, 8, &uval32) == 8) + { + objc_self = uval32[0]; + objc_selector = uval32[1]; + } + } +#elif defined (__powerpc__) || defined (__ppc__) + DNBRegisterValue r3; + DNBRegisterValue r4; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r3", &r3) && + DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r4", &r4)) + { + objc_self = r3.value.uint32; + objc_selector = r4.value.uint32; + } +#elif defined (__arm__) + DNBRegisterValue r0; + DNBRegisterValue r1; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r0", &r0) && + DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r1", &r1)) + { + objc_self = r0.value.uint32; + objc_selector = r1.value.uint32; + } +#else +#error undefined architecture +#endif + if (objc_selector != 0) + { + uint32_t isa = 0; + if (objc_self == 0) + { + profile_objc->SelectorHit(0, objc_selector); + } + else + if (DNBProcessMemoryRead(pid, (nub_addr_t)objc_self, sizeof(isa), &isa) == sizeof(isa)) + { + if (isa) + { + profile_objc->SelectorHit(isa, objc_selector); + } + else + { + profile_objc->SelectorHit(0, objc_selector); + } + } + } + + + // Dump stats if we are supposed to + if (profile_objc->ShouldDumpStats()) + { + profile_objc->DumpStats(pid, stdout); + return true; + } + + // Just let the target run again by returning false; + return false; +} + +nub_bool_t +ProfileObjectiveC::MessageSendSuperBreakpointCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *userData) +{ + ProfileObjectiveC *profile_objc = (ProfileObjectiveC*)userData; + + uint32_t hit_count = profile_objc->IncrementHitCount(); + if (hit_count == 1) + profile_objc->SetStartTime(); + +// printf("BreakID %u hit count is = %u\n", breakID, hc); + objc_class_ptr_t objc_super = 0; + objc_selector_t objc_selector = 0; +#if defined (__i386__) + DNBRegisterValue esp; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "esp", &esp)) + { + uint32_t uval32[2]; + if (DNBProcessMemoryRead(pid, esp.value.uint32 + 4, 8, &uval32) == 8) + { + objc_super = uval32[0]; + objc_selector = uval32[1]; + } + } +#elif defined (__powerpc__) || defined (__ppc__) + DNBRegisterValue r3; + DNBRegisterValue r4; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r3", &r3) && + DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r4", &r4)) + { + objc_super = r3.value.uint32; + objc_selector = r4.value.uint32; + } +#elif defined (__arm__) + DNBRegisterValue r0; + DNBRegisterValue r1; + if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r0", &r0) && + DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r1", &r1)) + { + objc_super = r0.value.uint32; + objc_selector = r1.value.uint32; + } +#else +#error undefined architecture +#endif + if (objc_selector != 0) + { + uint32_t isa = 0; + if (objc_super == 0) + { + profile_objc->SelectorHit(0, objc_selector); + } + else + if (DNBProcessMemoryRead(pid, (nub_addr_t)objc_super + 4, sizeof(isa), &isa) == sizeof(isa)) + { + if (isa) + { + profile_objc->SelectorHit(isa, objc_selector); + } + else + { + profile_objc->SelectorHit(0, objc_selector); + } + } + } + + // Dump stats if we are supposed to + if (profile_objc->ShouldDumpStats()) + { + profile_objc->DumpStats(pid, stdout); + return true; + } + + // Just let the target run again by returning false; + return false; +} + +void +ProfileObjectiveC::DumpStats(nub_process_t pid, FILE *f) +{ + if (f == NULL) + return; + + if (m_hit_count == 0) + return; + + ClassStatsMap::iterator class_pos; + ClassStatsMap::iterator class_end = m_objcStats.end(); + + struct timeval end_time; + gettimeofday(&end_time, NULL); + int64_t elapsed_usec = ((int64_t)(1000*1000))*((int64_t)end_time.tv_sec - (int64_t)m_begin_time.tv_sec) + ((int64_t)end_time.tv_usec - (int64_t)m_begin_time.tv_usec); + fprintf(f, "%u probe hits for %.2f hits/sec)\n", m_hit_count, (double)m_hit_count / (((double)elapsed_usec)/(1000000.0))); + + for (class_pos = m_objcStats.begin(); class_pos != class_end; ++class_pos) + { + SelectorHitCount::iterator sel_pos; + SelectorHitCount::iterator sel_end = class_pos->second.end(); + for (sel_pos = class_pos->second.begin(); sel_pos != sel_end; ++sel_pos) + { + struct objc_class objc_class; + uint32_t isa = class_pos->first; + uint32_t sel = sel_pos->first; + uint32_t sel_hit_count = sel_pos->second; + + if (isa != 0 && DNBProcessMemoryRead(pid, isa, sizeof(objc_class), &objc_class) == sizeof(objc_class)) + { + /* fprintf(f, "%#.8x\n isa = %p\n super_class = %p\n name = %p\n version = %lx\n info = %lx\ninstance_size = %lx\n ivars = %p\n methodLists = %p\n cache = %p\n protocols = %p\n", + arg1.value.pointer, + objc_class.isa, + objc_class.super_class, + objc_class.name, + objc_class.version, + objc_class.info, + objc_class.instance_size, + objc_class.ivars, + objc_class.methodLists, + objc_class.cache, + objc_class.protocols); */ + + // Print the class name + fprintf(f, "%6u hits for %c[", sel_hit_count, (objc_class.super_class == objc_class.isa ? '+' : '-')); + DNBPrintf(pid, INVALID_NUB_THREAD, (nub_addr_t)objc_class.name, f, "%s "); + } + else + { + fprintf(f, "%6u hits for [ ", sel_hit_count); + } + DNBPrintf(pid, INVALID_NUB_THREAD, sel, f, "%s]\n"); + } + } +} + diff --git a/lldb/tools/debugserver/source/ProfileObjectiveC.h b/lldb/tools/debugserver/source/ProfileObjectiveC.h new file mode 100644 index 000000000000..8a5c13db32ae --- /dev/null +++ b/lldb/tools/debugserver/source/ProfileObjectiveC.h @@ -0,0 +1,82 @@ +//===-- ProfileObjectiveC.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 10/4/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __ProfileObjectiveC_h__ +#define __ProfileObjectiveC_h__ + +#include "DNB.h" +#include "DNBRuntimeAction.h" +#include +#include + +class ProfileObjectiveC : public DNBRuntimeAction +{ +public: + ProfileObjectiveC(); + virtual ~ProfileObjectiveC(); + //------------------------------------------------------------------ + // DNBRuntimeAction required functions + //------------------------------------------------------------------ + virtual void Initialize(nub_process_t pid); + virtual void ProcessStateChanged(nub_state_t state); + virtual void SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos); + +protected: + typedef uint32_t objc_selector_t; + typedef uint32_t objc_class_ptr_t; + void Clear(); + static nub_bool_t MessageSendBreakpointCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *userData); + static nub_bool_t MessageSendSuperBreakpointCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *userData); + void DumpStats(nub_process_t pid, FILE *f); + void SetStartTime(); + void SelectorHit(objc_class_ptr_t isa, objc_selector_t sel); + typedef std::map SelectorHitCount; + typedef std::map ClassStatsMap; + typedef struct Probe + { + nub_addr_t addr; + nub_break_t breakID; + Probe() : addr(INVALID_NUB_ADDRESS), breakID(INVALID_NUB_BREAK_ID) {} + void Clear() + { + addr = INVALID_NUB_ADDRESS; + breakID = INVALID_NUB_BREAK_ID; + } + bool IsValid() const + { + return (addr != INVALID_NUB_ADDRESS) && (NUB_BREAK_ID_IS_VALID(breakID)); + } + }; + + uint32_t IncrementHitCount() { return ++m_hit_count; } + bool ShouldDumpStats() const { return m_dump_count && (m_hit_count % m_dump_count) == 0; } + + nub_process_t m_pid; + Probe m_objc_msgSend; + Probe m_objc_msgSendSuper; + uint32_t m_hit_count; // Number of times we have gotten one of our breakpoints hit + uint32_t m_dump_count; // Dump stats every time the hit count reaches a multiple of this value +#if defined (__powerpc__) || defined (__ppc__) + enum + { + k_opcode_size = 4 + }; + uint8_t m_objc_msgSend_opcode[k_opcode_size]; // Saved copy of first opcode in objc_msgSend + Probe m_objc_msgSend_rtp; // COMM page probe info for objc_msgSend +#endif + struct timeval m_begin_time; + ClassStatsMap m_objcStats; +}; + + +#endif // #ifndef __ProfileObjectiveC_h__ diff --git a/lldb/tools/debugserver/source/PseudoTerminal.cpp b/lldb/tools/debugserver/source/PseudoTerminal.cpp new file mode 100644 index 000000000000..278ab88828a7 --- /dev/null +++ b/lldb/tools/debugserver/source/PseudoTerminal.cpp @@ -0,0 +1,226 @@ +//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/8/08. +// +//===----------------------------------------------------------------------===// + +#include "PseudoTerminal.h" +#include +#include + +//---------------------------------------------------------------------- +// PseudoTerminal constructor +//---------------------------------------------------------------------- +PseudoTerminal::PseudoTerminal() : + m_master_fd(invalid_fd), + m_slave_fd(invalid_fd) +{ +} + +//---------------------------------------------------------------------- +// Destructor +// The master and slave file descriptors will get closed if they are +// valid. Call the ReleaseMasterFD()/ReleaseSlaveFD() member functions +// to release any file descriptors that are needed beyond the lifespan +// of this object. +//---------------------------------------------------------------------- +PseudoTerminal::~PseudoTerminal() +{ + CloseMaster(); + CloseSlave(); +} + +//---------------------------------------------------------------------- +// Close the master file descriptor if it is valid. +//---------------------------------------------------------------------- +void +PseudoTerminal::CloseMaster() +{ + if (m_master_fd > 0) + { + ::close (m_master_fd); + m_master_fd = invalid_fd; + } +} + +//---------------------------------------------------------------------- +// Close the slave file descriptor if it is valid. +//---------------------------------------------------------------------- +void +PseudoTerminal::CloseSlave() +{ + if (m_slave_fd > 0) + { + ::close (m_slave_fd); + m_slave_fd = invalid_fd; + } +} + +//---------------------------------------------------------------------- +// Open the first available pseudo terminal with OFLAG as the +// permissions. The file descriptor is store in the m_master_fd member +// variable and can be accessed via the MasterFD() or ReleaseMasterFD() +// accessors. +// +// Suggested value for oflag is O_RDWR|O_NOCTTY +// +// RETURNS: +// Zero when successful, non-zero indicating an error occurred. +//---------------------------------------------------------------------- +PseudoTerminal::Error +PseudoTerminal::OpenFirstAvailableMaster(int oflag) +{ + // Open the master side of a pseudo terminal + m_master_fd = ::posix_openpt (oflag); + if (m_master_fd < 0) + { + return err_posix_openpt_failed; + } + + // Grant access to the slave pseudo terminal + if (::grantpt (m_master_fd) < 0) + { + CloseMaster(); + return err_grantpt_failed; + } + + // Clear the lock flag on the slave pseudo terminal + if (::unlockpt (m_master_fd) < 0) + { + CloseMaster(); + return err_unlockpt_failed; + } + + return success; +} + +//---------------------------------------------------------------------- +// Open the slave pseudo terminal for the current master pseudo +// terminal. A master pseudo terminal should already be valid prior to +// calling this function (see PseudoTerminal::OpenFirstAvailableMaster()). +// The file descriptor is stored in the m_slave_fd member variable and +// can be accessed via the SlaveFD() or ReleaseSlaveFD() accessors. +// +// RETURNS: +// Zero when successful, non-zero indicating an error occurred. +//---------------------------------------------------------------------- +PseudoTerminal::Error +PseudoTerminal::OpenSlave(int oflag) +{ + CloseSlave(); + + // Open the master side of a pseudo terminal + const char *slave_name = SlaveName(); + + if (slave_name == NULL) + return err_ptsname_failed; + + m_slave_fd = ::open (slave_name, oflag); + + if (m_slave_fd < 0) + return err_open_slave_failed; + + return success; +} + + + +//---------------------------------------------------------------------- +// Get the name of the slave pseudo terminal. A master pseudo terminal +// should already be valid prior to calling this function (see +// PseudoTerminal::OpenFirstAvailableMaster()). +// +// RETURNS: +// NULL if no valid master pseudo terminal or if ptsname() fails. +// The name of the slave pseudo terminal as a NULL terminated C string +// that comes from static memory, so a copy of the string should be +// made as subsequent calls can change this value. +//---------------------------------------------------------------------- +const char* +PseudoTerminal::SlaveName() const +{ + if (m_master_fd < 0) + return NULL; + return ::ptsname (m_master_fd); +} + + +//---------------------------------------------------------------------- +// Fork a child process that and have its stdio routed to a pseudo +// terminal. +// +// In the parent process when a valid pid is returned, the master file +// descriptor can be used as a read/write access to stdio of the +// child process. +// +// In the child process the stdin/stdout/stderr will already be routed +// to the slave pseudo terminal and the master file descriptor will be +// closed as it is no longer needed by the child process. +// +// This class will close the file descriptors for the master/slave +// when the destructor is called, so be sure to call ReleaseMasterFD() +// or ReleaseSlaveFD() if any file descriptors are going to be used +// past the lifespan of this object. +// +// RETURNS: +// in the parent process: the pid of the child, or -1 if fork fails +// in the child process: zero +//---------------------------------------------------------------------- + +pid_t +PseudoTerminal::Fork(PseudoTerminal::Error& error) +{ + pid_t pid = invalid_pid; + error = OpenFirstAvailableMaster (O_RDWR|O_NOCTTY); + + if (error == 0) + { + // Successfully opened our master pseudo terminal + + pid = ::fork (); + if (pid < 0) + { + // Fork failed + error = err_fork_failed; + } + else if (pid == 0) + { + // Child Process + ::setsid(); + + error = OpenSlave (O_RDWR); + if (error == 0) + { + // Successfully opened slave + // We are done with the master in the child process so lets close it + CloseMaster (); + +#if defined (TIOCSCTTY) + // Acquire the controlling terminal + if (::ioctl (m_slave_fd, TIOCSCTTY, (char *)0) < 0) + error = err_failed_to_acquire_controlling_terminal; +#endif + // Duplicate all stdio file descriptors to the slave pseudo terminal + if (::dup2 (m_slave_fd, STDIN_FILENO) != STDIN_FILENO) + error = error ? error : err_dup2_failed_on_stdin; + if (::dup2 (m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) + error = error ? error : err_dup2_failed_on_stdout; + if (::dup2 (m_slave_fd, STDERR_FILENO) != STDERR_FILENO) + error = error ? error : err_dup2_failed_on_stderr; + } + } + else + { + // Parent Process + // Do nothing and let the pid get returned! + } + } + return pid; +} diff --git a/lldb/tools/debugserver/source/PseudoTerminal.h b/lldb/tools/debugserver/source/PseudoTerminal.h new file mode 100644 index 000000000000..1f09b4174521 --- /dev/null +++ b/lldb/tools/debugserver/source/PseudoTerminal.h @@ -0,0 +1,94 @@ +//===-- PseudoTerminal.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 1/8/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __PseudoTerminal_h__ +#define __PseudoTerminal_h__ + +#include +#include +#include + +class PseudoTerminal +{ +public: + enum { + invalid_fd = -1, + invalid_pid = -1 + }; + + typedef enum Error + { + success = 0, + err_posix_openpt_failed = -2, + err_grantpt_failed = -3, + err_unlockpt_failed = -4, + err_ptsname_failed = -5, + err_open_slave_failed = -6, + err_fork_failed = -7, + err_setsid_failed = -8, + err_failed_to_acquire_controlling_terminal = -9, + err_dup2_failed_on_stdin = -10, + err_dup2_failed_on_stdout = -11, + err_dup2_failed_on_stderr = -12 + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + PseudoTerminal (); + ~PseudoTerminal (); + + void CloseMaster (); + void CloseSlave (); + Error OpenFirstAvailableMaster (int oflag); + Error OpenSlave (int oflag); + int MasterFD () const { return m_master_fd; } + int SlaveFD () const { return m_slave_fd; } + int ReleaseMasterFD () + { + // Release ownership of the master pseudo terminal file + // descriptor without closing it. (the destructor for this + // class will close it otherwise!) + int fd = m_master_fd; + m_master_fd = invalid_fd; + return fd; + } + int ReleaseSlaveFD () + { + // Release ownership of the slave pseudo terminal file + // descriptor without closing it (the destructor for this + // class will close it otherwise!) + int fd = m_slave_fd; + m_slave_fd = invalid_fd; + return fd; + } + + const char* SlaveName () const; + + pid_t Fork(Error& error); +protected: + //------------------------------------------------------------------ + // Classes that inherit from PseudoTerminal can see and modify these + //------------------------------------------------------------------ + int m_master_fd; + int m_slave_fd; + +private: + //------------------------------------------------------------------ + // Outlaw copy and assignment constructors + //------------------------------------------------------------------ + PseudoTerminal(const PseudoTerminal& rhs); + PseudoTerminal& operator=(const PseudoTerminal& rhs); + +}; + +#endif // #ifndef __PseudoTerminal_h__ diff --git a/lldb/tools/debugserver/source/RNBContext.cpp b/lldb/tools/debugserver/source/RNBContext.cpp new file mode 100644 index 000000000000..7ef9d32c74e2 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBContext.cpp @@ -0,0 +1,230 @@ +//===-- RNBContext.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#include "RNBContext.h" +#include "RNBRemote.h" +#include "DNB.h" +#include "DNBLog.h" +#include "CFString.h" +#include + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RNBContext::~RNBContext() +{ + SetProcessID (INVALID_NUB_PROCESS); +} + +//---------------------------------------------------------------------- +// RNBContext constructor +//---------------------------------------------------------------------- + +const char * +RNBContext::EnvironmentAtIndex (int index) +{ + if (index < m_env_vec.size()) + return m_env_vec[index].c_str(); + else + return NULL; +} + + +const char * +RNBContext::ArgumentAtIndex (int index) +{ + if (index < m_arg_vec.size()) + return m_arg_vec[index].c_str(); + else + return NULL; +} + +void +RNBContext::SetProcessID (nub_process_t pid) +{ + // Delete and events we created + if (m_pid != INVALID_NUB_PROCESS) + { + StopProcessStatusThread (); + // Unregister this context as a client of the process's events. + } + // Assign our new process ID + m_pid = pid; + + if (pid != INVALID_NUB_PROCESS) + { + StartProcessStatusThread (); + } +} + +void +RNBContext::StartProcessStatusThread() +{ + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__); + if ((m_events.GetEventBits() & event_proc_thread_running) == 0) + { + int err = ::pthread_create (&m_pid_pthread, NULL, ThreadFunctionProcessStatus, this); + if (err == 0) + { + // Our thread was successfully kicked off, wait for it to + // set the started event so we can safely continue + m_events.WaitForSetEvents (event_proc_thread_running); + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!", __FUNCTION__); + } + else + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread failed to start: err = %i", __FUNCTION__, err); + m_events.ResetEvents (event_proc_thread_running); + m_events.SetEvents (event_proc_thread_exiting); + } + } +} + +void +RNBContext::StopProcessStatusThread() +{ + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__); + if ((m_events.GetEventBits() & event_proc_thread_running) == event_proc_thread_running) + { + struct timespec timeout_abstime; + DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0); + // Wait for 2 seconds for the rx thread to exit + if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting, &timeout_abstime) == RNBContext::event_proc_thread_exiting) + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread stopped as requeseted", __FUNCTION__); + } + else + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread did not stop in 2 seconds...", __FUNCTION__); + // Kill the RX thread??? + } + } +} + +//---------------------------------------------------------------------- +// This thread's sole purpose is to watch for any status changes in the +// child process. +//---------------------------------------------------------------------- +void* +RNBContext::ThreadFunctionProcessStatus(void *arg) +{ + RNBRemoteSP remoteSP(g_remoteSP); + RNBRemote* remote = remoteSP.get(); + if (remote == NULL) + return NULL; + RNBContext& ctx = remote->Context(); + + nub_process_t pid = ctx.ProcessID(); + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...", __FUNCTION__, arg, pid); + ctx.Events().SetEvents (RNBContext::event_proc_thread_running); + bool done = false; + while (!done) + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable, true)...", __FUNCTION__); + nub_event_t pid_status_event = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable, true, NULL); + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable, true) => 0x%8.8x", __FUNCTION__, pid_status_event); + + if (pid_status_event == 0) + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back from DNBProcessWaitForEvent....", __FUNCTION__, pid); + // done = true; + } + else + { + if (pid_status_event & eEventStdioAvailable) + { + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got stdio available event....", __FUNCTION__, pid); + ctx.Events().SetEvents (RNBContext::event_proc_stdio_available); + // Wait for the main thread to consume this notification if it requested we wait for it + ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available); + } + + + if (pid_status_event & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged)) + { + nub_state_t pid_state = DNBProcessGetState(pid); + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got process state change: %s", __FUNCTION__, pid, DNBStateAsString(pid_state)); + + // Let the main thread know there is a process state change to see + ctx.Events().SetEvents (RNBContext::event_proc_state_changed); + // Wait for the main thread to consume this notification if it requested we wait for it + ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed); + + switch (pid_state) + { + case eStateStopped: + break; + + case eStateInvalid: + case eStateExited: + done = true; + break; + } + } + + // Reset any events that we consumed. + DNBProcessResetEvents(pid, pid_status_event); + + } + } + DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...", __FUNCTION__, arg, pid); + ctx.Events().ResetEvents(event_proc_thread_running); + ctx.Events().SetEvents(event_proc_thread_exiting); + return NULL; +} + + +const char* +RNBContext::EventsAsString (nub_event_t events, std::string& s) +{ + s.clear(); + if (events & event_proc_state_changed) + s += "proc_state_changed "; + if (events & event_proc_thread_running) + s += "proc_thread_running "; + if (events & event_proc_thread_exiting) + s += "proc_thread_exiting "; + if (events & event_proc_stdio_available) + s += "proc_stdio_available "; + if (events & event_read_packet_available) + s += "read_packet_available "; + if (events & event_read_thread_running) + s += "read_thread_running "; + if (events & event_read_thread_running) + s += "read_thread_running "; + return s.c_str(); +} + +const char * +RNBContext::LaunchStatusAsString (std::string& s) +{ + s.clear(); + + const char *err_str = m_launch_status.AsString(); + if (err_str) + s = err_str; + else + { + char error_num_str[64]; + snprintf(error_num_str, sizeof(error_num_str), "%u", m_launch_status.Error()); + s = error_num_str; + } + return s.c_str(); +} + +bool +RNBContext::ProcessStateRunning() const +{ + nub_state_t pid_state = DNBProcessGetState(m_pid); + return pid_state == eStateRunning || pid_state == eStateStepping; +} diff --git a/lldb/tools/debugserver/source/RNBContext.h b/lldb/tools/debugserver/source/RNBContext.h new file mode 100644 index 000000000000..4b5f5ae2cc95 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBContext.h @@ -0,0 +1,123 @@ +//===-- RNBContext.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBContext_h__ +#define __RNBContext_h__ + +#include "RNBDefs.h" +#include "DNBError.h" +#include "PThreadEvent.h" +#include +#include + +class RNBContext +{ +public: + enum + { + event_proc_state_changed = 0x01, + event_proc_thread_running = 0x02, // Sticky + event_proc_thread_exiting = 0x04, + event_proc_stdio_available = 0x08, + event_read_packet_available = 0x10, + event_read_thread_running = 0x20, // Sticky + event_read_thread_exiting = 0x40, + + normal_event_bits = event_proc_state_changed | + event_proc_thread_exiting | + event_proc_stdio_available | + event_read_packet_available | + event_read_thread_exiting, + + sticky_event_bits = event_proc_thread_running | + event_read_thread_running, + + + all_event_bits = sticky_event_bits | normal_event_bits + } event_t; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RNBContext () : + m_pid(INVALID_NUB_PROCESS), + m_pid_stop_count(0), + m_events(0, all_event_bits), + m_pid_pthread(), + m_launch_status(), + m_arg_vec (), + m_env_vec () + { + } + + virtual ~RNBContext(); + + + nub_process_t ProcessID() const { return m_pid; } + bool HasValidProcessID() const { return m_pid != INVALID_NUB_PROCESS; } + void SetProcessID (nub_process_t pid); + nub_size_t GetProcessStopCount () const { return m_pid_stop_count; } + bool SetProcessStopCount (nub_size_t count) + { + // Returns true if this class' notion of the PID state changed + if (m_pid_stop_count == count) + return false; // Didn't change + m_pid_stop_count = count; + return true; // The stop count has changed. + } + + bool ProcessStateRunning() const; + PThreadEvent& Events( ) { return m_events; } + nub_event_t AllEventBits() const { return all_event_bits; } + nub_event_t NormalEventBits() const { return normal_event_bits; } + nub_event_t StickyEventBits() const { return sticky_event_bits; } + const char* EventsAsString (nub_event_t events, std::string& s); + + int ArgumentCount () const { return m_arg_vec.size(); } + const char * ArgumentAtIndex (int index); + void PushArgument (const char *arg) { if (arg) m_arg_vec.push_back (arg); } + void ClearArgv () { m_arg_vec.erase (m_arg_vec.begin(), m_arg_vec.end()); } + + int EnvironmentCount () const { return m_env_vec.size(); } + const char * EnvironmentAtIndex (int index); + void PushEnvironment (const char *arg) { if (arg) m_env_vec.push_back (arg); } + void ClearEnvironment () { m_env_vec.erase (m_env_vec.begin(), m_env_vec.end()); } + DNBError& LaunchStatus () { return m_launch_status; } + const char * LaunchStatusAsString (std::string& s); + nub_launch_flavor_t LaunchFlavor () const { return m_launch_flavor; } + void SetLaunchFlavor (nub_launch_flavor_t flavor) { m_launch_flavor = flavor; } +protected: + //------------------------------------------------------------------ + // Classes that inherit from RNBContext can see and modify these + //------------------------------------------------------------------ + nub_process_t m_pid; + nub_size_t m_pid_stop_count; + PThreadEvent m_events; // Threaded events that we can wait for + pthread_t m_pid_pthread; + nub_launch_flavor_t m_launch_flavor; // How to launch our inferior process + DNBError m_launch_status; // This holds the status from the last launch attempt. + std::vector m_arg_vec; + std::vector m_env_vec; // This will be unparsed - entries FOO=value + + void StartProcessStatusThread(); + void StopProcessStatusThread(); + static void* ThreadFunctionProcessStatus(void *arg); + +private: + //------------------------------------------------------------------ + // Outlaw copy and assignment operators + //------------------------------------------------------------------ + RNBContext(const RNBContext& rhs); + RNBContext& operator=(const RNBContext& rhs); +}; + +#endif // #ifndef __RNBContext_h__ diff --git a/lldb/tools/debugserver/source/RNBDefs.h b/lldb/tools/debugserver/source/RNBDefs.h new file mode 100644 index 000000000000..f603d3d22a66 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBDefs.h @@ -0,0 +1,78 @@ +//===-- RNBDefs.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/14/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBDefs_h__ +#define __RNBDefs_h__ + +#include "DNBDefs.h" + +#include // for std::tr1::shared_ptr + +extern "C" const unsigned char debugserverVersionString[]; +extern "C" const double debugserverVersionNumber; +#define DEBUGSERVER_PROGRAM_NAME "debugserver" +#define DEBUGSERVER_VERSION_STR debugserverVersionString +#define DEBUGSERVER_VERSION_NUM debugserverVersionNumber + +#if defined (__i386__) + +#define RNB_ARCH "i386" + +#elif defined (__x86_64__) + +#define RNB_ARCH "x86_64" + +#elif defined (__ppc64__) + +#define RNB_ARCH "ppc64" + +#elif defined (__powerpc__) || defined (__ppc__) + +#define RNB_ARCH "ppc" + +#elif defined (__arm__) + +#define RNB_ARCH "armv6" + +#else + +#error undefined architecture + +#endif + +class RNBRemote; +typedef std::tr1::shared_ptr RNBRemoteSP; + +typedef enum +{ + rnb_success = 0, + rnb_err = 1, + rnb_not_connected = 2 +} rnb_err_t; + +// Log bits +// reserve low bits for DNB +#define LOG_RNB_MINIMAL ((LOG_LO_USER) << 0) // Minimal logging (min verbosity) +#define LOG_RNB_MEDIUM ((LOG_LO_USER) << 1) // Medium logging (med verbosity) +#define LOG_RNB_MAX ((LOG_LO_USER) << 2) // Max logging (max verbosity) +#define LOG_RNB_COMM ((LOG_LO_USER) << 3) // Log communications (RNBSocket) +#define LOG_RNB_REMOTE ((LOG_LO_USER) << 4) // Log remote (RNBRemote) +#define LOG_RNB_EVENTS ((LOG_LO_USER) << 5) // Log events (PThreadEvents) +#define LOG_RNB_PROC ((LOG_LO_USER) << 6) // Log process state (Process thread) +#define LOG_RNB_PACKETS ((LOG_LO_USER) << 7) // Log gdb remote packets +#define LOG_RNB_ALL (~((LOG_LO_USER) - 1)) +#define LOG_RNB_DEFAULT (LOG_RNB_ALL) + +extern RNBRemoteSP g_remoteSP; + +#endif // #ifndef __RNBDefs_h__ diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp new file mode 100644 index 000000000000..3ac3ee9178f3 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -0,0 +1,3187 @@ +//===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#include "RNBRemote.h" + +#include +#include +#include +#include +#include + +#include "DNB.h" +#include "DNBLog.h" +#include "DNBThreadResumeActions.h" +#include "RNBContext.h" +#include "RNBServices.h" +#include "RNBSocket.h" +#include "StringExtractor.h" + +#include +#include + +#include // for endianness predefines + +//---------------------------------------------------------------------- +// std::iostream formatting macros +//---------------------------------------------------------------------- +#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)((uint8_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 RAWHEX_SIZE(x, sz) RAW_HEXBASE << std::setw((sz)) << (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)) << "" +// Class to handle communications via gdb remote protocol. + +extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args); + +RNBRemote::RNBRemote (bool use_native_regs) : + m_ctx(), + m_comm(), + m_extended_mode(false), + m_noack_mode(false), + m_continue_thread(-1), + m_thread(-1), + m_mutex(), + m_packets_recvd(0), + m_packets(), + m_rx_packets(), + m_rx_partial_data(), + m_rx_pthread(0), + m_breakpoints(), + m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4), + m_use_native_regs (use_native_regs) +{ + DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__); + CreatePacketTable (); +} + + +RNBRemote::~RNBRemote() +{ + DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__); + StopReadRemoteDataThread(); +} + +void +RNBRemote::CreatePacketTable () +{ + // Step required to add new packets: + // 1 - Add new enumeration to RNBRemote::PacketEnum + // 2 - Create a the RNBRemote::HandlePacket_ function if a new function is needed + // 3 - Register the Packet definition with any needed callbacks in this fucntion + // - If no response is needed for a command, then use NULL for the normal callback + // - If the packet is not supported while the target is running, use NULL for the async callback + // 4 - If the packet is a standard packet (starts with a '$' character + // followed by the payload and then '#' and checksum, then you are done + // else go on to step 5 + // 5 - if the packet is a fixed length packet: + // - modify the switch statement for the first character in the payload + // in RNBRemote::CommDataReceived so it doesn't reject the new packet + // type as invalid + // - modify the switch statement for the first character in the payload + // in RNBRemote::GetPacketPayload and make sure the payload of the packet + // is returned correctly + + std::vector &t = m_packets; + t.push_back (Packet (ack, NULL, NULL, "+", "ACK")); + t.push_back (Packet (nack, NULL, NULL, "-", "!ACK")); + t.push_back (Packet (read_memory, &RNBRemote::HandlePacket_m, NULL, "m", "Read memory")); + t.push_back (Packet (read_register, &RNBRemote::HandlePacket_p, NULL, "p", "Read one register")); + t.push_back (Packet (read_general_regs, &RNBRemote::HandlePacket_g, NULL, "g", "Read registers")); + t.push_back (Packet (write_memory, &RNBRemote::HandlePacket_M, NULL, "M", "Write memory")); + t.push_back (Packet (write_register, &RNBRemote::HandlePacket_P, NULL, "P", "Write one register")); + t.push_back (Packet (write_general_regs, &RNBRemote::HandlePacket_G, NULL, "G", "Write registers")); + t.push_back (Packet (insert_mem_bp, &RNBRemote::HandlePacket_z, NULL, "Z0", "Insert memory breakpoint")); + t.push_back (Packet (remove_mem_bp, &RNBRemote::HandlePacket_z, NULL, "z0", "Remove memory breakpoint")); + t.push_back (Packet (single_step, &RNBRemote::HandlePacket_s, NULL, "s", "Single step")); + t.push_back (Packet (cont, &RNBRemote::HandlePacket_c, NULL, "c", "continue")); + t.push_back (Packet (single_step_with_sig, &RNBRemote::HandlePacket_S, NULL, "S", "Single step with signal")); + t.push_back (Packet (set_thread, &RNBRemote::HandlePacket_H, NULL, "H", "Set thread")); + t.push_back (Packet (halt, &RNBRemote::HandlePacket_last_signal, &RNBRemote::HandlePacket_stop_process, "\x03", "^C")); +// t.push_back (Packet (use_extended_mode, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode")); + t.push_back (Packet (why_halted, &RNBRemote::HandlePacket_last_signal, NULL, "?", "Why did target halt")); + t.push_back (Packet (set_argv, &RNBRemote::HandlePacket_A, NULL, "A", "Set argv")); +// t.push_back (Packet (set_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear breakpoint")); + t.push_back (Packet (continue_with_sig, &RNBRemote::HandlePacket_C, NULL, "C", "Continue with signal")); + t.push_back (Packet (detach, &RNBRemote::HandlePacket_D, NULL, "D", "Detach gdb from remote system")); +// t.push_back (Packet (step_inferior_one_cycle, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one clock cycle")); +// t.push_back (Packet (signal_and_step_inf_one_cycle, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then step one clock cyle")); + t.push_back (Packet (kill, &RNBRemote::HandlePacket_k, NULL, "k", "Kill")); +// t.push_back (Packet (restart, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior")); +// t.push_back (Packet (search_mem_backwards, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory backwards")); + t.push_back (Packet (thread_alive_p, &RNBRemote::HandlePacket_T, NULL, "T", "Is thread alive")); + t.push_back (Packet (vattach, &RNBRemote::HandlePacket_v, NULL, "vAttach", "Attach to a new process")); + t.push_back (Packet (vattachwait, &RNBRemote::HandlePacket_v, NULL, "vAttachWait", "Wait for a process to start up then attach to it")); + t.push_back (Packet (vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, "vCont;", "Verbose resume with thread actions")); + t.push_back (Packet (vcont_list_actions, &RNBRemote::HandlePacket_v, NULL, "vCont?", "List valid continue-with-thread-actions actions")); + // The X packet doesn't currently work. If/when it does, remove the line above and uncomment out the line below +// t.push_back (Packet (write_data_to_memory, &RNBRemote::HandlePacket_X, NULL, "X", "Write data to memory")); +// t.push_back (Packet (insert_hardware_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z1", "Insert hardware breakpoint")); +// t.push_back (Packet (remove_hardware_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z1", "Remove hardware breakpoint")); +// t.push_back (Packet (insert_write_watch_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z2", "Insert write watchpoint")); +// t.push_back (Packet (remove_write_watch_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z2", "Remove write watchpoint")); +// t.push_back (Packet (insert_read_watch_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z3", "Insert read watchpoint")); +// t.push_back (Packet (remove_read_watch_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z3", "Remove read watchpoint")); +// t.push_back (Packet (insert_access_watch_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z4", "Insert access watchpoint")); +// t.push_back (Packet (remove_access_watch_bp, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z4", "Remove access watchpoint")); + t.push_back (Packet (query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL, "qC", "Query current thread ID")); +// t.push_back (Packet (query_memory_crc, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qCRC:", "Compute CRC of memory region")); + t.push_back (Packet (query_thread_ids_first, &RNBRemote::HandlePacket_qThreadInfo, NULL, "qfThreadInfo", "Get list of active threads (first req)")); + t.push_back (Packet (query_thread_ids_subsequent, &RNBRemote::HandlePacket_qThreadInfo, NULL, "qsThreadInfo", "Get list of active threads (subsequent req)")); + // APPLE LOCAL: qThreadStopInfo + // syntax: qThreadStopInfoTTTT + // TTTT is hex thread ID + t.push_back (Packet (query_thread_stop_info, &RNBRemote::HandlePacket_qThreadStopInfo, NULL, "qThreadStopInfo", "Get detailed info on why the specified thread stopped")); + t.push_back (Packet (query_thread_extra_info, &RNBRemote::HandlePacket_qThreadExtraInfo,NULL, "qThreadExtraInfo", "Get printable status of a thread")); +// t.push_back (Packet (query_image_offsets, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset of loaded program")); + t.push_back (Packet (query_launch_success, &RNBRemote::HandlePacket_qLaunchSuccess,NULL, "qLaunchSuccess", "Report the success or failure of the launch attempt")); + t.push_back (Packet (query_register_info, &RNBRemote::HandlePacket_qRegisterInfo, NULL, "qRegisterInfo", "Dynamically discover remote register context information.")); + t.push_back (Packet (query_shlib_notify_info_addr, &RNBRemote::HandlePacket_qShlibInfoAddr,NULL, "qShlibInfoAddr", "Returns the address that contains info needed for getting shared library notifications")); + t.push_back (Packet (query_step_packet_supported, &RNBRemote::HandlePacket_qStepPacketSupported,NULL, "qStepPacketSupported", "Replys with OK if the 's' packet is supported.")); + t.push_back (Packet (query_host_info, &RNBRemote::HandlePacket_qHostInfo, NULL, "qHostInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); +// t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qSymbol", "Notify that host debugger is ready to do symbol lookups")); + t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_Q , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); + t.push_back (Packet (set_logging_mode, &RNBRemote::HandlePacket_Q , NULL, "QSetLogging:", "Request that the " DEBUGSERVER_PROGRAM_NAME " set its logging mode bits")); + t.push_back (Packet (set_max_packet_size, &RNBRemote::HandlePacket_Q , NULL, "QSetMaxPacketSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle")); + t.push_back (Packet (set_max_payload_size, &RNBRemote::HandlePacket_Q , NULL, "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized payload gdb can handle")); + t.push_back (Packet (set_environment_variable, &RNBRemote::HandlePacket_Q , NULL, "QEnvironment:", "Add an environment variable to the inferior's environment")); +// t.push_back (Packet (pass_signals_to_inferior, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify which signals are passed to the inferior")); + t.push_back (Packet (allocate_memory, &RNBRemote::HandlePacket_AllocateMemory, NULL, "_M", "Allocate memory in the inferior process.")); + t.push_back (Packet (deallocate_memory, &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m", "Deallocate memory in the inferior process.")); +} + + +void +RNBRemote::FlushSTDIO () +{ + if (m_ctx.HasValidProcessID()) + { + nub_process_t pid = m_ctx.ProcessID(); + char buf[256]; + nub_size_t count; + do + { + count = DNBProcessGetAvailableSTDOUT(pid, buf, sizeof(buf)); + if (count > 0) + { + SendSTDOUTPacket (buf, count); + } + } while (count > 0); + + do + { + count = DNBProcessGetAvailableSTDERR(pid, buf, sizeof(buf)); + if (count > 0) + { + SendSTDERRPacket (buf, count); + } + } while (count > 0); + } +} + +rnb_err_t +RNBRemote::SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer) +{ + std::ostringstream packet_sstrm; + // Append the header cstr if there was one + if (header && header[0]) + packet_sstrm << header; + nub_size_t i; + const uint8_t *ubuf8 = (const uint8_t *)buf; + for (i=0; iabbrev.size(), it->abbrev) == 0) + break; + } + + // A packet we don't have an entry for. This can happen when we + // get a packet that we don't know about or support. We just reply + // accordingly and go on. + if (it == m_packets.end ()) + { + DNBLogThreadedIf (LOG_RNB_PACKETS, "unimplemented packet: '%s'", payload.c_str()); + HandlePacket_UNIMPLEMENTED(payload.c_str()); + return rnb_err; + } + else + { + packet_info = *it; + packet_payload = payload; + } + } + return err; +} + +rnb_err_t +RNBRemote::HandleAsyncPacket(PacketEnum *type) +{ + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + static DNBTimer g_packetTimer(true); + rnb_err_t err = rnb_err; + std::string packet_data; + RNBRemote::Packet packet_info; + err = GetPacket (packet_data, packet_info, false); + + if (err == rnb_success) + { + if (!packet_data.empty() && isprint(packet_data[0])) + DNBLogThreadedIf (LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (\"%s\");", packet_data.c_str()); + else + DNBLogThreadedIf (LOG_RNB_REMOTE | LOG_RNB_PACKETS, "HandleAsyncPacket (%s);", packet_info.printable_name.c_str()); + + HandlePacketCallback packet_callback = packet_info.async; + if (packet_callback != NULL) + { + if (type != NULL) + *type = packet_info.type; + return (this->*packet_callback)(packet_data.c_str()); + } + } + + return err; +} + +rnb_err_t +RNBRemote::HandleReceivedPacket(PacketEnum *type) +{ + static DNBTimer g_packetTimer(true); + + // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + rnb_err_t err = rnb_err; + std::string packet_data; + RNBRemote::Packet packet_info; + err = GetPacket (packet_data, packet_info, false); + + if (err == rnb_success) + { + DNBLogThreadedIf (LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");", packet_data.c_str()); + HandlePacketCallback packet_callback = packet_info.normal; + if (packet_callback != NULL) + { + if (type != NULL) + *type = packet_info.type; + return (this->*packet_callback)(packet_data.c_str()); + } + else + { + // Do not fall through to end of this function, if we have valid + // packet_info and it has a NULL callback, then we need to respect + // that it may not want any response or anything to be done. + return err; + } + } + return rnb_err; +} + +void +RNBRemote::CommDataReceived(const std::string& new_data) +{ + // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + { + // Put the packet data into the buffer in a thread safe fashion + PThreadMutex::Locker locker(m_mutex); + + std::string data; + // See if we have any left over data from a previous call to this + // function? + if (!m_rx_partial_data.empty()) + { + // We do, so lets start with that data + data.swap(m_rx_partial_data); + } + // Append the new incoming data + data += new_data; + + // Parse up the packets into gdb remote packets + uint32_t idx = 0; + const size_t data_size = data.size(); + + while (idx < data_size) + { + // end_idx must be one past the last valid packet byte. Start + // it off with an invalid value that is the same as the current + // index. + size_t end_idx = idx; + + switch (data[idx]) + { + case '+': // Look for ack + case '-': // Look for cancel + case '\x03': // ^C to halt target + end_idx = idx + 1; // The command is one byte long... + break; + + case '$': + // Look for a standard gdb packet? + end_idx = data.find('#', idx + 1); + if (end_idx == std::string::npos || end_idx + 2 > data_size) + { + end_idx = std::string::npos; + } + else + { + // Add two for the checksum bytes + end_idx += 4; + } + break; + + default: + break; + } + + if (end_idx == std::string::npos) + { + // Not all data may be here for the packet yet, save it for + // next time through this function. + m_rx_partial_data += data.substr(idx); + //DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for later[%u, npos): '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, idx, m_rx_partial_data.c_str()); + idx = end_idx; + } + else + if (idx < end_idx) + { + m_packets_recvd++; + // Hack to get rid of initial '+' ACK??? + if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+') + { + //DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first ACK away....[%u, npos): '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, idx); + } + else + { + // We have a valid packet... + m_rx_packets.push_back(data.substr(idx, end_idx - idx)); + DNBLogThreadedIf (LOG_RNB_PACKETS, "getpkt: %s", m_rx_packets.back().c_str()); + } + idx = end_idx; + } + else + { + DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s tossing junk byte at %c",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, data[idx]); + idx = idx + 1; + } + } + } + + if (!m_rx_packets.empty()) + { + // Let the main thread know we have received a packet + + //DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s called events.SetEvent(RNBContext::event_read_packet_available)", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + PThreadEvent& events = m_ctx.Events(); + events.SetEvents (RNBContext::event_read_packet_available); + } +} + +rnb_err_t +RNBRemote::GetCommData () +{ + // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + std::string comm_data; + rnb_err_t err = m_comm.Read (comm_data); + if (err == rnb_success) + { + if (!comm_data.empty()) + CommDataReceived (comm_data); + } + return err; +} + +void +RNBRemote::StartReadRemoteDataThread() +{ + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + PThreadEvent& events = m_ctx.Events(); + if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0) + { + events.ResetEvents (RNBContext::event_read_thread_exiting); + int err = ::pthread_create (&m_rx_pthread, NULL, ThreadFunctionReadRemoteData, this); + if (err == 0) + { + // Our thread was successfully kicked off, wait for it to + // set the started event so we can safely continue + events.WaitForSetEvents (RNBContext::event_read_thread_running); + } + else + { + events.ResetEvents (RNBContext::event_read_thread_running); + events.SetEvents (RNBContext::event_read_thread_exiting); + } + } +} + +void +RNBRemote::StopReadRemoteDataThread() +{ + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); + PThreadEvent& events = m_ctx.Events(); + if ((events.GetEventBits() & RNBContext::event_read_thread_running) == RNBContext::event_read_thread_running) + { + m_comm.Disconnect(true); + struct timespec timeout_abstime; + DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0); + + // Wait for 2 seconds for the remote data thread to exit + if (events.WaitForSetEvents(RNBContext::event_read_thread_exiting, &timeout_abstime) == 0) + { + // Kill the remote data thread??? + } + } +} + + +void* +RNBRemote::ThreadFunctionReadRemoteData(void *arg) +{ + // Keep a shared pointer reference so this doesn't go away on us before the thread is killed. + DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting...", __FUNCTION__, arg); + RNBRemoteSP remoteSP(g_remoteSP); + if (remoteSP.get() != NULL) + { + RNBRemote* remote = remoteSP.get(); + PThreadEvent& events = remote->Context().Events(); + events.SetEvents (RNBContext::event_read_thread_running); + // START: main receive remote command thread loop + bool done = false; + while (!done) + { + rnb_err_t err = remote->GetCommData(); + + switch (err) + { + case rnb_success: + break; + + default: + case rnb_err: + DNBLogThreadedIf (LOG_RNB_REMOTE, "RNBSocket::GetCommData returned error %u", err); + done = true; + break; + + case rnb_not_connected: + DNBLogThreadedIf (LOG_RNB_REMOTE, "RNBSocket::GetCommData returned not connected..."); + done = true; + break; + } + } + // START: main receive remote command thread loop + events.ResetEvents (RNBContext::event_read_thread_running); + events.SetEvents (RNBContext::event_read_thread_exiting); + } + DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting...", __FUNCTION__, arg); + return NULL; +} + + + +/* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes + (8-bit bytes). + This encoding uses 0x7d ('}') as an escape character for 0x7d ('}'), + 0x23 ('#'), and 0x24 ('$'). + LEN is the number of bytes to be processed. If a character is escaped, + it is 2 characters for LEN. A LEN of -1 means encode-until-nul-byte + (end of string). */ + +std::vector +decode_binary_data (const char *str, int len) +{ + std::vector bytes; + if (len == 0) + { + return bytes; + } + if (len == -1) + len = strlen (str); + + while (len--) + { + unsigned char c = *str; + if (c == 0x7d && len > 0) + { + len--; + str++; + c ^= 0x20; + } + bytes.push_back (c); + } + return bytes; +} + +typedef struct register_map_entry +{ + uint32_t gdb_regnum; // gdb register number + uint32_t gdb_size; // gdb register size in bytes (can be greater than or less than to debugnub size...) + const char * gdb_name; // gdb register name + DNBRegisterInfo nub_info; // debugnub register info + const uint8_t* fail_value; // Value to print if case we fail to reg this register (if this is NULL, we will return an error) + int expedite; // expedite delivery of this register in last stop reply packets +} register_map_entry_t; + + + +// If the notion of registers differs from what is handed out by the +// architecture, then flavors can be defined here. + +static const uint32_t MAX_REGISTER_BYTE_SIZE = 16; +static const uint8_t k_zero_bytes[MAX_REGISTER_BYTE_SIZE] = {0}; +static std::vector g_dynamic_register_map; +static register_map_entry_t *g_reg_entries = NULL; +static size_t g_num_reg_entries = 0; + +static void +RegisterEntryNotAvailable (register_map_entry_t *reg_entry) +{ + reg_entry->fail_value = k_zero_bytes; + reg_entry->nub_info.set = INVALID_NUB_REGNUM; + reg_entry->nub_info.reg = INVALID_NUB_REGNUM; + reg_entry->nub_info.name = NULL; + reg_entry->nub_info.alt = NULL; + reg_entry->nub_info.type = InvalidRegType; + reg_entry->nub_info.format = InvalidRegFormat; + reg_entry->nub_info.size = 0; + reg_entry->nub_info.offset = 0; + reg_entry->nub_info.reg_gcc = INVALID_NUB_REGNUM; + reg_entry->nub_info.reg_dwarf = INVALID_NUB_REGNUM; + reg_entry->nub_info.reg_generic = INVALID_NUB_REGNUM; + reg_entry->nub_info.reg_gdb = INVALID_NUB_REGNUM; +} + +#if defined (__arm__) + +//---------------------------------------------------------------------- +// ARM regiseter sets as gdb knows them +//---------------------------------------------------------------------- + +register_map_entry_t +g_gdb_register_map_arm[] = +{ + { 0, 4, "r0", {0}, NULL, 1}, + { 1, 4, "r1", {0}, NULL, 1}, + { 2, 4, "r2", {0}, NULL, 1}, + { 3, 4, "r3", {0}, NULL, 1}, + { 4, 4, "r4", {0}, NULL, 1}, + { 5, 4, "r5", {0}, NULL, 1}, + { 6, 4, "r6", {0}, NULL, 1}, + { 7, 4, "r7", {0}, NULL, 1}, + { 8, 4, "r8", {0}, NULL, 1}, + { 9, 4, "r9", {0}, NULL, 1}, + { 10, 4, "r10", {0}, NULL, 1}, + { 11, 4, "r11", {0}, NULL, 1}, + { 12, 4, "r12", {0}, NULL, 1}, + { 13, 4, "sp", {0}, NULL, 1}, + { 14, 4, "lr", {0}, NULL, 1}, + { 15, 4, "pc", {0}, NULL, 1}, + { 16, 12, "f0", NULL, k_zero_bytes, 0}, + { 17, 12, "f1", NULL, k_zero_bytes, 0}, + { 18, 12, "f2", NULL, k_zero_bytes, 0}, + { 19, 12, "f3", NULL, k_zero_bytes, 0}, + { 20, 12, "f4", NULL, k_zero_bytes, 0}, + { 21, 12, "f5", NULL, k_zero_bytes, 0}, + { 22, 12, "f6", NULL, k_zero_bytes, 0}, + { 23, 12, "f7", NULL, k_zero_bytes, 0}, + { 24, 4, "fps", {0}, NULL, 0}, + { 25, 4,"cpsr", {0}, NULL, 1}, + { 26, 4, "s0", {0}, NULL, 0}, + { 27, 4, "s1", {0}, NULL, 0}, + { 28, 4, "s2", {0}, NULL, 0}, + { 29, 4, "s3", {0}, NULL, 0}, + { 30, 4, "s4", {0}, NULL, 0}, + { 31, 4, "s5", {0}, NULL, 0}, + { 32, 4, "s6", {0}, NULL, 0}, + { 33, 4, "s7", {0}, NULL, 0}, + { 34, 4, "s8", {0}, NULL, 0}, + { 35, 4, "s9", {0}, NULL, 0}, + { 36, 4, "s10", {0}, NULL, 0}, + { 37, 4, "s11", {0}, NULL, 0}, + { 38, 4, "s12", {0}, NULL, 0}, + { 39, 4, "s13", {0}, NULL, 0}, + { 40, 4, "s14", {0}, NULL, 0}, + { 41, 4, "s15", {0}, NULL, 0}, + { 42, 4, "s16", {0}, NULL, 0}, + { 43, 4, "s17", {0}, NULL, 0}, + { 44, 4, "s18", {0}, NULL, 0}, + { 45, 4, "s19", {0}, NULL, 0}, + { 46, 4, "s20", {0}, NULL, 0}, + { 47, 4, "s21", {0}, NULL, 0}, + { 48, 4, "s22", {0}, NULL, 0}, + { 49, 4, "s23", {0}, NULL, 0}, + { 50, 4, "s24", {0}, NULL, 0}, + { 51, 4, "s25", {0}, NULL, 0}, + { 52, 4, "s26", {0}, NULL, 0}, + { 53, 4, "s27", {0}, NULL, 0}, + { 54, 4, "s28", {0}, NULL, 0}, + { 55, 4, "s29", {0}, NULL, 0}, + { 56, 4, "s30", {0}, NULL, 0}, + { 57, 4, "s31", {0}, NULL, 0}, + { 58, 4, "fpscr", {0}, NULL, 0} +}; + +void +RNBRemote::InitializeRegisters (int use_native_registers) +{ + if (use_native_registers) + { + RNBRemote::InitializeNativeRegisters(); + } + else + { + const size_t num_regs = sizeof (g_gdb_register_map_arm) / sizeof (register_map_entry_t); + for (uint32_t i=0; i 0 && reg_sets != NULL); + + uint32_t regnum = 0; + for (nub_size_t set = 0; set < num_reg_sets; ++set) + { + if (reg_sets[set].registers == NULL) + continue; + + for (uint32_t reg=0; reg < reg_sets[set].num_registers; ++reg) + { + register_map_entry_t reg_entry = { + regnum++, // register number starts at zero and goes up with no gaps + reg_sets[set].registers[reg].size, // register size in bytes + reg_sets[set].registers[reg].name, // register name + reg_sets[set].registers[reg], // DNBRegisterInfo + NULL, // Value to print if case we fail to reg this register (if this is NULL, we will return an error) + reg_sets[set].registers[reg].reg_generic != INVALID_NUB_REGNUM}; + + g_dynamic_register_map.push_back (reg_entry); + } + } + g_reg_entries = g_dynamic_register_map.data(); + g_num_reg_entries = g_dynamic_register_map.size(); + } +} + + +const register_map_entry_t * +register_mapping_by_regname (const char *n) +{ + for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) + { + if (strcmp (g_reg_entries[reg].gdb_name, n) == 0) + return &g_reg_entries[reg]; + } + return NULL; +} + +/* The inferior has stopped executing; send a packet + to gdb to let it know. */ + +void +RNBRemote::NotifyThatProcessStopped (void) +{ + RNBRemote::HandlePacket_last_signal (""); + return; +} + + +/* `A arglen,argnum,arg,...' + Update the inferior context CTX with the program name and arg + list. + The documentation for this packet is underwhelming but my best reading + of this is that it is a series of (len, position #, arg)'s, one for + each argument with "arg" ``hex encoded'' (two 0-9a-f chars?). + Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either + is sufficient to get around the "," position separator escape issue. + + e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is + + 6,0,676462,4,1,2d71,10,2,612e6f7574 + + Note that "argnum" and "arglen" are numbers in base 10. Again, that's + not documented either way but I'm assuming it's so. */ + +rnb_err_t +RNBRemote::HandlePacket_A (const char *p) +{ + if (p == NULL || *p == '\0') + { + return HandlePacket_ILLFORMED ("Null packet for 'A' pkt"); + } + p++; + if (p == '\0' || !isdigit (*p)) + { + return HandlePacket_ILLFORMED ("arglen not specified on 'A' pkt"); + } + + /* I promise I don't modify it anywhere in this function. strtoul()'s + 2nd arg has to be non-const which makes it problematic to step + through the string easily. */ + char *buf = const_cast(p); + + RNBContext& ctx = Context(); + + while (*buf != '\0') + { + int arglen, argnum; + std::string arg; + char *c; + + errno = 0; + arglen = strtoul (buf, &c, 10); + if (errno != 0 && arglen == 0) + { + return HandlePacket_ILLFORMED ("arglen not a number on 'A' pkt"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED ("arglen not followed by comma on 'A' pkt"); + } + buf = c + 1; + + errno = 0; + argnum = strtoul (buf, &c, 10); + if (errno != 0 && argnum == 0) + { + return HandlePacket_ILLFORMED ("argnum not a number on 'A' pkt"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED ("arglen not followed by comma on 'A' pkt"); + } + buf = c + 1; + + c = buf; + buf = buf + arglen; + while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0') + { + char smallbuf[3]; + smallbuf[0] = *c; + smallbuf[1] = *(c + 1); + smallbuf[2] = '\0'; + + errno = 0; + int ch = strtoul (smallbuf, NULL, 16); + if (errno != 0 && ch == 0) + { + return HandlePacket_ILLFORMED ("non-hex char in arg on 'A' pkt"); + } + + arg.push_back(ch); + c += 2; + } + + ctx.PushArgument (arg.c_str()); + if (*buf == ',') + buf++; + } + SendPacket ("OK"); + + return rnb_success; +} + +/* `H c t' + Set the thread for subsequent actions; 'c' for step/continue ops, + 'g' for other ops. -1 means all threads, 0 means any thread. */ + +rnb_err_t +RNBRemote::HandlePacket_H (const char *p) +{ + p++; // skip 'H' + if (*p != 'c' && *p != 'g') + { + return HandlePacket_ILLFORMED ("Missing 'c' or 'g' type in H packet"); + } + + if (!m_ctx.HasValidProcessID()) + { + // We allow gdb to connect to a server that hasn't started running + // the target yet. gdb still wants to ask questions about it and + // freaks out if it gets an error. So just return OK here. + } + + errno = 0; + nub_thread_t tid = strtoul (p + 1, NULL, 16); + if (errno != 0 && tid == 0) + { + return HandlePacket_ILLFORMED ("Invalid thread number in H packet"); + } + if (*p == 'c') + SetContinueThread (tid); + if (*p == 'g') + SetCurrentThread (tid); + + return SendPacket ("OK"); +} + + +rnb_err_t +RNBRemote::HandlePacket_qLaunchSuccess (const char *p) +{ + if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Error() == 0) + return SendPacket("OK"); + std::ostringstream ret_str; + std::string status_str; + ret_str << "E" << m_ctx.LaunchStatusAsString(status_str); + + return SendPacket (ret_str.str()); +} + +rnb_err_t +RNBRemote::HandlePacket_qShlibInfoAddr (const char *p) +{ + if (m_ctx.HasValidProcessID()) + { + nub_addr_t shlib_info_addr = DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID()); + if (shlib_info_addr != INVALID_NUB_ADDRESS) + { + std::ostringstream ostrm; + ostrm << RAW_HEXBASE << shlib_info_addr; + return SendPacket (ostrm.str ()); + } + } + return SendPacket ("E44"); +} + +rnb_err_t +RNBRemote::HandlePacket_qStepPacketSupported (const char *p) +{ + // Normally the "s" packet is mandatory, yet in gdb when using ARM, they + // get around the need for this packet by implementing software single + // stepping from gdb. Current versions of debugserver do support the "s" + // packet, yet some older versions do not. We need a way to tell if this + // packet is supported so we can disable software single stepping in gdb + // for remote targets (so the "s" packet will get used). + return SendPacket("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_qThreadStopInfo (const char *p) +{ + p += strlen ("qThreadStopInfo"); + nub_thread_t tid = strtoul(p, 0, 16); + return SendStopReplyPacketForThread (tid); +} + +rnb_err_t +RNBRemote::HandlePacket_qThreadInfo (const char *p) +{ + // We allow gdb to connect to a server that hasn't started running + // the target yet. gdb still wants to ask questions about it and + // freaks out if it gets an error. So just return OK here. + nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("OK"); + + // Only "qfThreadInfo" and "qsThreadInfo" get into this function so + // we only need to check the second byte to tell which is which + if (p[1] == 'f') + { + nub_size_t numthreads = DNBProcessGetNumThreads (pid); + std::ostringstream ostrm; + ostrm << "m"; + bool first = true; + for (nub_size_t i = 0; i < numthreads; ++i) + { + if (first) + first = false; + else + ostrm << ","; + nub_thread_t th = DNBProcessGetThreadAtIndex (pid, i); + ostrm << std::hex << th; + } + return SendPacket (ostrm.str ()); + } + else + { + return SendPacket ("l"); + } +} + +rnb_err_t +RNBRemote::HandlePacket_qThreadExtraInfo (const char *p) +{ + // We allow gdb to connect to a server that hasn't started running + // the target yet. gdb still wants to ask questions about it and + // freaks out if it gets an error. So just return OK here. + nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("OK"); + + /* This is supposed to return a string like 'Runnable' or + 'Blocked on Mutex'. + The returned string is formatted like the "A" packet - a + sequence of letters encoded in as 2-hex-chars-per-letter. */ + p += strlen ("qThreadExtraInfo"); + if (*p++ != ',') + return HandlePacket_ILLFORMED ("Ill formed qThreadExtraInfo packet"); + errno = 0; + nub_thread_t tid = strtoul (p, NULL, 16); + if (errno != 0 && tid == 0) + { + return HandlePacket_ILLFORMED ("Invalid thread number in qThreadExtraInfo packet"); + } + + const char * threadInfo = DNBThreadGetInfo(pid, tid); + if (threadInfo != NULL && threadInfo[0]) + { + return SendHexEncodedBytePacket(NULL, threadInfo, strlen(threadInfo), NULL); + } + else + { + // "OK" == 4f6b + // Return "OK" as a ASCII hex byte stream if things go wrong + return SendPacket ("4f6b"); + } + + return SendPacket (""); +} + +rnb_err_t +RNBRemote::HandlePacket_qC (const char *p) +{ + nub_process_t pid; + std::ostringstream rep; + // If we haven't run the process yet, we tell the debugger the + // pid is 0. That way it can know to tell use to run later on. + if (m_ctx.HasValidProcessID()) + pid = m_ctx.ProcessID(); + else + pid = 0; + rep << "QC" << std::hex << pid; + return SendPacket (rep.str()); +} + +rnb_err_t +RNBRemote::HandlePacket_qRegisterInfo (const char *p) +{ + p += strlen ("qRegisterInfo"); + + nub_size_t num_reg_sets = 0; + const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo (&num_reg_sets); + uint32_t reg_num = strtoul(p, 0, 16); + + if (reg_num < g_num_reg_entries) + { + const register_map_entry_t *reg_entry = &g_reg_entries[reg_num]; + std::ostringstream ostrm; + ostrm << "name:" << reg_entry->gdb_name << ';'; + + if (reg_entry->nub_info.name && ::strcmp (reg_entry->gdb_name, reg_entry->nub_info.name)) + ostrm << "alt-name:" << reg_entry->nub_info.name << ';'; + else if (reg_entry->nub_info.alt && ::strcmp (reg_entry->gdb_name, reg_entry->nub_info.alt)) + ostrm << "alt-name:" << reg_entry->nub_info.alt << ';'; + + ostrm << "bitsize:" << std::dec << reg_entry->gdb_size * 8 << ';'; + ostrm << "offset:" << std::dec << reg_entry->nub_info.offset << ';'; + + switch (reg_entry->nub_info.type) + { + case Uint: ostrm << "encoding:uint;"; break; + case Sint: ostrm << "encoding:sint;"; break; + case IEEE754: ostrm << "encoding:ieee754;"; break; + case Vector: ostrm << "encoding:vector;"; break; + } + + switch (reg_entry->nub_info.format) + { + case Binary: ostrm << "format:binary;"; break; + case Decimal: ostrm << "format:decimal;"; break; + case Hex: ostrm << "format:hex;"; break; + case Float: ostrm << "format:float;"; break; + case VectorOfSInt8: ostrm << "format:vector-sint8;"; break; + case VectorOfUInt8: ostrm << "format:vector-uint8;"; break; + case VectorOfSInt16: ostrm << "format:vector-sint16;"; break; + case VectorOfUInt16: ostrm << "format:vector-uint16;"; break; + case VectorOfSInt32: ostrm << "format:vector-sint32;"; break; + case VectorOfUInt32: ostrm << "format:vector-uint32;"; break; + case VectorOfFloat32: ostrm << "format:vector-float32;"; break; + case VectorOfUInt128: ostrm << "format:vector-uint128;"; break; + }; + + if (reg_set_info && reg_entry->nub_info.set < num_reg_sets) + ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';'; + + + if (g_reg_entries != g_dynamic_register_map.data()) + { + if (reg_entry->nub_info.reg_gdb != INVALID_NUB_REGNUM && reg_entry->nub_info.reg_gdb != reg_num) + { + printf("register %s is getting gdb reg_num of %u when the register info says %u\n", + reg_entry->gdb_name, reg_num, reg_entry->nub_info.reg_gdb); + } + } + + if (reg_entry->nub_info.reg_gcc != INVALID_NUB_REGNUM) + ostrm << "gcc:" << std::dec << reg_entry->nub_info.reg_gcc << ';'; + + if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM) + ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';'; + + + switch (reg_entry->nub_info.reg_generic) + { + case GENERIC_REGNUM_FP: ostrm << "generic:fp;"; break; + case GENERIC_REGNUM_PC: ostrm << "generic:pc;"; break; + case GENERIC_REGNUM_SP: ostrm << "generic:sp;"; break; + case GENERIC_REGNUM_RA: ostrm << "generic:ra;"; break; + case GENERIC_REGNUM_FLAGS: ostrm << "generic:flags;"; break; + default: break; + } + + return SendPacket (ostrm.str ()); + } + return SendPacket ("E45"); +} + + +/* This expects a packet formatted like + + QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE; + + with the "QSetLogging:" already removed from the start. Maybe in the + future this packet will include other keyvalue pairs like + + QSetLogging:bitmask=LOG_ALL;mode=asl; + */ + +rnb_err_t +set_logging (const char *p) +{ + int bitmask = 0; + while (p && *p != '\0') + { + if (strncmp (p, "bitmask=", sizeof ("bitmask=") - 1) == 0) + { + p += sizeof ("bitmask=") - 1; + while (p && *p != '\0' && *p != ';') + { + if (*p == '|') + p++; + if (strncmp (p, "LOG_VERBOSE", sizeof ("LOG_VERBOSE") - 1) == 0) + { + p += sizeof ("LOG_VERBOSE") - 1; + bitmask |= LOG_VERBOSE; + } + else if (strncmp (p, "LOG_PROCESS", sizeof ("LOG_PROCESS") - 1) == 0) + { + p += sizeof ("LOG_PROCESS") - 1; + bitmask |= LOG_PROCESS; + } + else if (strncmp (p, "LOG_THREAD", sizeof ("LOG_THREAD") - 1) == 0) + { + p += sizeof ("LOG_THREAD") - 1; + bitmask |= LOG_THREAD; + } + else if (strncmp (p, "LOG_EXCEPTIONS", sizeof ("LOG_EXCEPTIONS") - 1) == 0) + { + p += sizeof ("LOG_EXCEPTIONS") - 1; + bitmask |= LOG_EXCEPTIONS; + } + else if (strncmp (p, "LOG_SHLIB", sizeof ("LOG_SHLIB") - 1) == 0) + { + p += sizeof ("LOG_SHLIB") - 1; + bitmask |= LOG_SHLIB; + } + else if (strncmp (p, "LOG_MEMORY", sizeof ("LOG_MEMORY") - 1) == 0) + { + p += sizeof ("LOG_MEMORY") - 1; + bitmask |= LOG_MEMORY; + } + else if (strncmp (p, "LOG_MEMORY_DATA_SHORT", sizeof ("LOG_MEMORY_DATA_SHORT") - 1) == 0) + { + p += sizeof ("LOG_MEMORY_DATA_SHORT") - 1; + bitmask |= LOG_MEMORY_DATA_SHORT; + } + else if (strncmp (p, "LOG_MEMORY_DATA_LONG", sizeof ("LOG_MEMORY_DATA_LONG") - 1) == 0) + { + p += sizeof ("LOG_MEMORY_DATA_LONG") - 1; + bitmask |= LOG_MEMORY_DATA_LONG; + } + else if (strncmp (p, "LOG_BREAKPOINTS", sizeof ("LOG_BREAKPOINTS") - 1) == 0) + { + p += sizeof ("LOG_BREAKPOINTS") - 1; + bitmask |= LOG_BREAKPOINTS; + } + else if (strncmp (p, "LOG_ALL", sizeof ("LOG_ALL") - 1) == 0) + { + p += sizeof ("LOG_ALL") - 1; + bitmask |= LOG_ALL; + } + else if (strncmp (p, "LOG_EVENTS", sizeof ("LOG_EVENTS") - 1) == 0) + { + p += sizeof ("LOG_EVENTS") - 1; + bitmask |= LOG_EVENTS; + } + else if (strncmp (p, "LOG_DEFAULT", sizeof ("LOG_DEFAULT") - 1) == 0) + { + p += sizeof ("LOG_DEFAULT") - 1; + bitmask |= LOG_DEFAULT; + } + else if (strncmp (p, "LOG_NONE", sizeof ("LOG_NONE") - 1) == 0) + { + p += sizeof ("LOG_NONE") - 1; + bitmask = 0; + } + else if (strncmp (p, "LOG_RNB_MINIMAL", sizeof ("LOG_RNB_MINIMAL") - 1) == 0) + { + p += sizeof ("LOG_RNB_MINIMAL") - 1; + bitmask |= LOG_RNB_MINIMAL; + } + else if (strncmp (p, "LOG_RNB_MEDIUM", sizeof ("LOG_RNB_MEDIUM") - 1) == 0) + { + p += sizeof ("LOG_RNB_MEDIUM") - 1; + bitmask |= LOG_RNB_MEDIUM; + } + else if (strncmp (p, "LOG_RNB_MAX", sizeof ("LOG_RNB_MAX") - 1) == 0) + { + p += sizeof ("LOG_RNB_MAX") - 1; + bitmask |= LOG_RNB_MAX; + } + else if (strncmp (p, "LOG_RNB_COMM", sizeof ("LOG_RNB_COMM") - 1) == 0) + { + p += sizeof ("LOG_RNB_COMM") - 1; + bitmask |= LOG_RNB_COMM; + } + else if (strncmp (p, "LOG_RNB_REMOTE", sizeof ("LOG_RNB_REMOTE") - 1) == 0) + { + p += sizeof ("LOG_RNB_REMOTE") - 1; + bitmask |= LOG_RNB_REMOTE; + } + else if (strncmp (p, "LOG_RNB_EVENTS", sizeof ("LOG_RNB_EVENTS") - 1) == 0) + { + p += sizeof ("LOG_RNB_EVENTS") - 1; + bitmask |= LOG_RNB_EVENTS; + } + else if (strncmp (p, "LOG_RNB_PROC", sizeof ("LOG_RNB_PROC") - 1) == 0) + { + p += sizeof ("LOG_RNB_PROC") - 1; + bitmask |= LOG_RNB_PROC; + } + else if (strncmp (p, "LOG_RNB_PACKETS", sizeof ("LOG_RNB_PACKETS") - 1) == 0) + { + p += sizeof ("LOG_RNB_PACKETS") - 1; + bitmask |= LOG_RNB_PACKETS; + } + else if (strncmp (p, "LOG_RNB_ALL", sizeof ("LOG_RNB_ALL") - 1) == 0) + { + p += sizeof ("LOG_RNB_ALL") - 1; + bitmask |= LOG_RNB_ALL; + } + else if (strncmp (p, "LOG_RNB_DEFAULT", sizeof ("LOG_RNB_DEFAULT") - 1) == 0) + { + p += sizeof ("LOG_RNB_DEFAULT") - 1; + bitmask |= LOG_RNB_DEFAULT; + } + else if (strncmp (p, "LOG_RNB_NONE", sizeof ("LOG_RNB_NONE") - 1) == 0) + { + p += sizeof ("LOG_RNB_NONE") - 1; + bitmask = 0; + } + else + { + /* Unrecognized logging bit; ignore it. */ + const char *c = strchr (p, '|'); + if (c) + { + p = c; + } + else + { + c = strchr (p, ';'); + if (c) + { + p = c; + } + else + { + // Improperly terminated word; just go to end of str + p = strchr (p, '\0'); + } + } + } + } + // Did we get a properly formatted logging bitmask? + if (*p == ';') + { + // Enable DNB logging + DNBLogSetLogCallback(ASLLogCallback, NULL); + DNBLogSetLogMask (bitmask); + p++; + } + } + // We're not going to support logging to a file for now. All logging + // goes through ASL. +#if 0 + else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0) + { + p += sizeof ("mode=") - 1; + if (strncmp (p, "asl;", sizeof ("asl;") - 1) == 0) + { + DNBLogToASL (); + p += sizeof ("asl;") - 1; + } + else if (strncmp (p, "file;", sizeof ("file;") - 1) == 0) + { + DNBLogToFile (); + p += sizeof ("file;") - 1; + } + else + { + // Ignore unknown argument + const char *c = strchr (p, ';'); + if (c) + p = c + 1; + else + p = strchr (p, '\0'); + } + } + else if (strncmp (p, "filename=", sizeof ("filename=") - 1) == 0) + { + p += sizeof ("filename=") - 1; + const char *c = strchr (p, ';'); + if (c == NULL) + { + c = strchr (p, '\0'); + continue; + } + char *fn = (char *) alloca (c - p + 1); + strncpy (fn, p, c - p); + fn[c - p] = '\0'; + + // A file name of "asl" is special and is another way to indicate + // that logging should be done via ASL, not by file. + if (strcmp (fn, "asl") == 0) + { + DNBLogToASL (); + } + else + { + FILE *f = fopen (fn, "w"); + if (f) + { + DNBLogSetLogFile (f); + DNBEnableLogging (f, DNBLogGetLogMask ()); + DNBLogToFile (); + } + } + p = c + 1; + } +#endif /* #if 0 to enforce ASL logging only. */ + else + { + // Ignore unknown argument + const char *c = strchr (p, ';'); + if (c) + p = c + 1; + else + p = strchr (p, '\0'); + } + } + + return rnb_success; +} + + + +rnb_err_t +RNBRemote::HandlePacket_Q (const char *p) +{ + if (p == NULL || strlen (p) <= 1) + { + return HandlePacket_ILLFORMED ("No subtype specified in Q packet"); + } + + /* Switch to no-ack protocol mode after the "OK" packet is sent + and the ack for that comes back from gdb. */ + + if (strcmp (p, "QStartNoAckMode") == 0) + { + rnb_err_t result = SendPacket ("OK"); + m_noack_mode = true; + return result; + } + + if (strncmp (p, "QSetLogging:", sizeof ("QSetLogging:") - 1) == 0) + { + p += sizeof ("QSetLogging:") - 1; + rnb_err_t result = set_logging (p); + if (result == rnb_success) + return SendPacket ("OK"); + else + return SendPacket ("E35"); + } + + /* The number of characters in a packet payload that gdb is + prepared to accept. The packet-start char, packet-end char, + 2 checksum chars and terminating null character are not included + in this size. */ + if (strncmp (p, "QSetMaxPayloadSize:", sizeof ("QSetMaxPayloadSize:") - 1) == 0) + { + p += sizeof ("QSetMaxPayloadSize:") - 1; + errno = 0; + uint32_t size = strtoul (p, NULL, 16); + if (errno != 0 && size == 0) + { + return HandlePacket_ILLFORMED ("Invalid length in QSetMaxPayloadSize packet"); + } + m_max_payload_size = size; + return SendPacket ("OK"); + } + + /* This tells us the largest packet that gdb can handle. + i.e. the size of gdb's packet-reading buffer. + QSetMaxPayloadSize is preferred because it is less ambiguous. */ + + if (strncmp (p, "QSetMaxPacketSize:", sizeof ("QSetMaxPacketSize:") - 1) == 0) + { + p += sizeof ("QSetMaxPacketSize:") - 1; + errno = 0; + uint32_t size = strtoul (p, NULL, 16); + if (errno != 0 && size == 0) + { + return HandlePacket_ILLFORMED ("Invalid length in QSetMaxPacketSize packet"); + } + m_max_payload_size = size - 5; + return SendPacket ("OK"); + } + + /* This sets the environment for the target program. The packet is of the form: + + QEnvironment:VARIABLE=VALUE + + */ + + if (strncmp (p, "QEnvironment:", sizeof ("QEnvironment:") - 1) == 0) + { + DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"", + (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p); + + p += sizeof ("QEnvironment:") - 1; + RNBContext& ctx = Context(); + + ctx.PushEnvironment (p); + return SendPacket ("OK"); + } + + // Unrecognized Q packet + return SendPacket (""); +} + +void +append_hex_value (std::ostream& ostrm, const uint8_t* buf, size_t buf_size, bool swap) +{ + int i; + if (swap) + { + for (i = buf_size-1; i >= 0; i--) + ostrm << RAWHEX8(buf[i]); + } + else + { + for (i = 0; i < buf_size; i++) + ostrm << RAWHEX8(buf[i]); + } +} + + +void +register_value_in_hex_fixed_width +( + std::ostream& ostrm, + nub_process_t pid, + nub_thread_t tid, + const register_map_entry_t* reg + ) +{ + if (reg != NULL) + { + DNBRegisterValue val; + if (DNBThreadGetRegisterValueByID (pid, tid, reg->nub_info.set, reg->nub_info.reg, &val)) + { + append_hex_value (ostrm, val.value.v_uint8, reg->gdb_size, false); + } + else + { + // If we fail to read a regiser value, check if it has a default + // fail value. If it does, return this instead in case some of + // the registers are not available on the current system. + if (reg->gdb_size > 0) + { + if (reg->fail_value != NULL) + { + append_hex_value (ostrm, reg->fail_value, reg->gdb_size, false); + } + else + { + std::basic_string zeros(reg->gdb_size, '\0'); + append_hex_value (ostrm, zeros.data(), zeros.size(), false); + } + } + } + } +} + + +void +gdb_regnum_with_fixed_width_hex_register_value +( + std::ostream& ostrm, + nub_process_t pid, + nub_thread_t tid, + const register_map_entry_t* reg + ) +{ + // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX + // gdb register number, and VVVVVVVV is the correct number of hex bytes + // as ASCII for the register value. + if (reg != NULL) + { + ostrm << RAWHEX8(reg->gdb_regnum) << ':'; + register_value_in_hex_fixed_width (ostrm, pid, tid, reg); + ostrm << ';'; + } +} + +rnb_err_t +RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid) +{ + const nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket("E50"); + + struct DNBThreadStopInfo tid_stop_info; + + /* Fill the remaining space in this packet with as many registers + as we can stuff in there. */ + + if (DNBThreadGetStopReason (pid, tid, &tid_stop_info)) + { + std::ostringstream ostrm; + // Output the T packet with the thread + ostrm << 'T'; + int signum = tid_stop_info.details.signal.signo; + DNBLogThreadedIf (LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, tid_stop_info.details.signal.signo, tid_stop_info.details.exception.type); + + // Translate any mach exceptions to gdb versions, unless they are + // common exceptions like a breakpoint or a soft signal. + switch (tid_stop_info.details.exception.type) + { + default: signum = 0; break; + case EXC_BREAKPOINT: signum = SIGTRAP; break; + case EXC_BAD_ACCESS: signum = TARGET_EXC_BAD_ACCESS; break; + case EXC_BAD_INSTRUCTION: signum = TARGET_EXC_BAD_INSTRUCTION; break; + case EXC_ARITHMETIC: signum = TARGET_EXC_ARITHMETIC; break; + case EXC_EMULATION: signum = TARGET_EXC_EMULATION; break; + case EXC_SOFTWARE: + if (tid_stop_info.details.exception.data_count == 2 && + tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL) + signum = tid_stop_info.details.exception.data[1]; + else + signum = TARGET_EXC_SOFTWARE; + break; + } + + ostrm << RAWHEX8(signum & 0xff); + + ostrm << std::hex << "thread:" << tid << ';'; + + const char *thread_name = DNBThreadGetName (pid, tid); + if (thread_name && thread_name[0]) + ostrm << std::hex << "name:" << thread_name << ';'; + + thread_identifier_info_data_t thread_ident_info; + if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info)) + { + if (thread_ident_info.dispatch_qaddr != 0) + ostrm << std::hex << "dispatchqaddr:" << thread_ident_info.dispatch_qaddr << ';'; + } + DNBRegisterValue reg_value; + for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) + { + if (g_reg_entries[reg].expedite) + { + if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, ®_value)) + continue; + + gdb_regnum_with_fixed_width_hex_register_value (ostrm, pid, tid, &g_reg_entries[reg]); + } + } + + if (tid_stop_info.details.exception.type) + { + ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ";"; + ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ";"; + for (int i = 0; i < tid_stop_info.details.exception.data_count; ++i) + ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ";"; + } + return SendPacket (ostrm.str ()); + } + return SendPacket("E51"); +} + +/* `?' + The stop reply packet - tell gdb what the status of the inferior is. + Often called the questionmark_packet. */ + +rnb_err_t +RNBRemote::HandlePacket_last_signal (const char *unused) +{ + if (!m_ctx.HasValidProcessID()) + { + // Inferior is not yet specified/running + return SendPacket ("E02"); + } + + nub_process_t pid = m_ctx.ProcessID(); + nub_state_t pid_state = DNBProcessGetState (pid); + + switch (pid_state) + { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + return rnb_success; // Ignore + + case eStateSuspended: + case eStateStopped: + case eStateCrashed: + { + nub_thread_t tid = DNBProcessGetCurrentThread (pid); + // Make sure we set the current thread so g and p packets return + // the data the gdb will expect. + SetCurrentThread (tid); + + SendStopReplyPacketForThread (tid); + } + break; + + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + { + char pid_exited_packet[16] = ""; + int pid_status = 0; + // Process exited with exit status + if (!DNBProcessGetExitStatus(pid, &pid_status)) + pid_status = 0; + + if (pid_status) + { + if (WIFEXITED (pid_status)) + snprintf (pid_exited_packet, sizeof(pid_exited_packet), "W%02x", WEXITSTATUS (pid_status)); + else if (WIFSIGNALED (pid_status)) + snprintf (pid_exited_packet, sizeof(pid_exited_packet), "X%02x", WEXITSTATUS (pid_status)); + else if (WIFSTOPPED (pid_status)) + snprintf (pid_exited_packet, sizeof(pid_exited_packet), "S%02x", WSTOPSIG (pid_status)); + } + + // If we have an empty exit packet, lets fill one in to be safe. + if (!pid_exited_packet[0]) + { + strncpy (pid_exited_packet, "W00", sizeof(pid_exited_packet)-1); + pid_exited_packet[sizeof(pid_exited_packet)-1] = '\0'; + } + + return SendPacket (pid_exited_packet); + } + break; + } + return rnb_success; +} + +rnb_err_t +RNBRemote::HandlePacket_M (const char *p) +{ + if (p == NULL || p[0] == '\0' || strlen (p) < 3) + { + return HandlePacket_ILLFORMED ("Too short M packet"); + } + + char *c; + p++; + errno = 0; + nub_addr_t addr = strtoull (p, &c, 16); + if (errno != 0 && addr == 0) + { + return HandlePacket_ILLFORMED ("Invalid address in M packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED ("Comma sep missing in M packet"); + } + + /* Advance 'p' to the length part of the packet. */ + p += (c - p) + 1; + + errno = 0; + uint32_t length = strtoul (p, &c, 16); + if (errno != 0 && length == 0) + { + return HandlePacket_ILLFORMED ("Invalid length in M packet"); + } + if (length == 0) + { + return SendPacket ("OK"); + } + + if (*c != ':') + { + return HandlePacket_ILLFORMED ("Missing colon in M packet"); + } + /* Advance 'p' to the data part of the packet. */ + p += (c - p) + 1; + + int datalen = strlen (p); + if (datalen & 0x1) + { + return HandlePacket_ILLFORMED ("Uneven # of hex chars for data in M packet"); + } + if (datalen == 0) + { + return SendPacket ("OK"); + } + + uint8_t *buf = (uint8_t *) alloca (datalen / 2); + uint8_t *i = buf; + + while (*p != '\0' && *(p + 1) != '\0') + { + char hexbuf[3]; + hexbuf[0] = *p; + hexbuf[1] = *(p + 1); + hexbuf[2] = '\0'; + errno = 0; + uint8_t byte = strtoul (hexbuf, NULL, 16); + if (errno != 0 && byte == 0) + { + return HandlePacket_ILLFORMED ("Invalid hex byte in M packet"); + } + *i++ = byte; + p += 2; + } + + nub_size_t wrote = DNBProcessMemoryWrite (m_ctx.ProcessID(), addr, length, buf); + if (wrote != length) + return SendPacket ("E09"); + else + return SendPacket ("OK"); +} + + +rnb_err_t +RNBRemote::HandlePacket_m (const char *p) +{ + if (p == NULL || p[0] == '\0' || strlen (p) < 3) + { + return HandlePacket_ILLFORMED ("Too short m packet"); + } + + char *c; + p++; + errno = 0; + nub_addr_t addr = strtoull (p, &c, 16); + if (errno != 0 && addr == 0) + { + return HandlePacket_ILLFORMED ("Invalid address in m packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED ("Comma sep missing in m packet"); + } + + /* Advance 'p' to the length part of the packet. */ + p += (c - p) + 1; + + errno = 0; + uint32_t length = strtoul (p, NULL, 16); + if (errno != 0 && length == 0) + { + return HandlePacket_ILLFORMED ("Invalid length in m packet"); + } + if (length == 0) + { + return SendPacket (""); + } + + uint8_t buf[length]; + int bytes_read = DNBProcessMemoryRead (m_ctx.ProcessID(), addr, length, buf); + if (bytes_read == 0) + { + return SendPacket ("E08"); + } + + // "The reply may contain fewer bytes than requested if the server was able + // to read only part of the region of memory." + length = bytes_read; + + std::ostringstream ostrm; + for (int i = 0; i < length; i++) + ostrm << RAWHEX8(buf[i]); + return SendPacket (ostrm.str ()); +} + +rnb_err_t +RNBRemote::HandlePacket_X (const char *p) +{ + if (p == NULL || p[0] == '\0' || strlen (p) < 3) + { + return HandlePacket_ILLFORMED ("Too short X packet"); + } + + char *c; + p++; + errno = 0; + nub_addr_t addr = strtoull (p, &c, 16); + if (errno != 0 && addr == 0) + { + return HandlePacket_ILLFORMED ("Invalid address in X packet"); + } + if (*c != ',') + { + return HandlePacket_ILLFORMED ("Comma sep missing in X packet"); + } + + /* Advance 'p' to the length part of the packet. */ + p += (c - p) + 1; + + errno = 0; + int length = strtoul (p, NULL, 16); + if (errno != 0 && length == 0) + { + return HandlePacket_ILLFORMED ("Invalid length in m packet"); + } + + // I think gdb sends a zero length write request to test whether this + // packet is accepted. + if (length == 0) + { + return SendPacket ("OK"); + } + + std::vector data = decode_binary_data (c, -1); + std::vector::const_iterator it; + uint8_t *buf = (uint8_t *) alloca (data.size ()); + uint8_t *i = buf; + for (it = data.begin (); it != data.end (); ++it) + { + *i++ = *it; + } + + nub_size_t wrote = DNBProcessMemoryWrite (m_ctx.ProcessID(), addr, data.size(), buf); + if (wrote != data.size ()) + return SendPacket ("E08"); + return SendPacket ("OK"); +} + +/* `g' -- read registers + Get the contents of the registers for the current thread, + send them to gdb. + Should the setting of the Hg packet determine which thread's registers + are returned? */ + +rnb_err_t +RNBRemote::HandlePacket_g (const char *p) +{ + std::ostringstream ostrm; + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E11"); + } + nub_process_t pid = m_ctx.ProcessID (); + nub_thread_t tid = GetCurrentThread(); + + if (m_use_native_regs) + { + // Get the register context size first by calling with NULL buffer + nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0); + if (reg_ctx_size) + { + // Now allocate enough space for the entire register context + std::vector reg_ctx; + reg_ctx.resize(reg_ctx_size); + // Now read the register context + reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, ®_ctx[0], reg_ctx.size()); + if (reg_ctx_size) + { + append_hex_value (ostrm, reg_ctx.data(), reg_ctx.size(), false); + return SendPacket (ostrm.str ()); + } + } + } + + for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) + register_value_in_hex_fixed_width (ostrm, pid, tid, &g_reg_entries[reg]); + + return SendPacket (ostrm.str ()); +} + +/* `G XXX...' -- write registers + How is the thread for these specified, beyond "the current thread"? + Does gdb actually use the Hg packet to set this? */ + +rnb_err_t +RNBRemote::HandlePacket_G (const char *p) +{ + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E11"); + } + StringExtractor packet(p); + packet.SetFilePos(1); // Skip the 'G' + + nub_process_t pid = m_ctx.ProcessID(); + nub_thread_t tid = GetCurrentThread(); + + if (m_use_native_regs) + { + // Get the register context size first by calling with NULL buffer + nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0); + if (reg_ctx_size) + { + // Now allocate enough space for the entire register context + std::vector reg_ctx; + reg_ctx.resize(reg_ctx_size); + + if (packet.GetHexBytes (®_ctx[0], reg_ctx.size(), 0xcc) == reg_ctx.size()) + { + // Now write the register context + reg_ctx_size = DNBThreadSetRegisterContext(pid, tid, reg_ctx.data(), reg_ctx.size()); + if (reg_ctx_size == reg_ctx.size()) + return SendPacket ("OK"); + else + return SendPacket ("E55"); + } + } + } + + + DNBRegisterValue reg_value; + for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) + { + const register_map_entry_t *reg_entry = &g_reg_entries[reg]; + + reg_value.info = reg_entry->nub_info; + if (packet.GetHexBytes (reg_value.value.v_sint8, reg_entry->gdb_size, 0xcc) != reg_entry->gdb_size) + break; + + if (!DNBThreadSetRegisterValueByID (pid, tid, reg_entry->nub_info.set, reg_entry->nub_info.reg, ®_value)) + return SendPacket ("E15"); + } + return SendPacket ("OK"); +} + +static bool +RNBRemoteShouldCancelCallback (void *not_used) +{ + RNBRemoteSP remoteSP(g_remoteSP); + if (remoteSP.get() != NULL) + { + RNBRemote* remote = remoteSP.get(); + if (remote->Comm().IsConnected()) + return false; + else + return true; + } + return true; +} + + +// FORMAT: _MXXXXXX,PPP +// XXXXXX: big endian hex chars +// PPP: permissions can be any combo of r w x chars +// +// RESPONSE: XXXXXX +// XXXXXX: hex address of the newly allocated memory +// EXX: error code +// +// EXAMPLES: +// _M123000,rw +// _M123000,rwx +// _M123000,xw + +rnb_err_t +RNBRemote::HandlePacket_AllocateMemory (const char *p) +{ + StringExtractor packet (p); + packet.SetFilePos(2); // Skip the "_M" + + nub_addr_t size = packet.GetHexMaxU64 (StringExtractor::BigEndian, 0); + if (size != 0) + { + if (packet.GetChar() == ',') + { + uint32_t permissions = 0; + char ch; + bool success = true; + while (success && (ch = packet.GetChar()) != '\0') + { + switch (ch) + { + case 'r': permissions |= eMemoryPermissionsReadable; break; + case 'w': permissions |= eMemoryPermissionsWritable; break; + case 'x': permissions |= eMemoryPermissionsExecutable; break; + default: success = false; break; + } + } + + if (success) + { + nub_addr_t addr = DNBProcessMemoryAllocate (m_ctx.ProcessID(), size, permissions); + if (addr != INVALID_NUB_ADDRESS) + { + std::ostringstream ostrm; + ostrm << RAW_HEXBASE << addr; + return SendPacket (ostrm.str ()); + } + } + } + } + return SendPacket ("E53"); +} + +// FORMAT: _mXXXXXX +// XXXXXX: address that was previosly allocated +// +// RESPONSE: XXXXXX +// OK: address was deallocated +// EXX: error code +// +// EXAMPLES: +// _m123000 + +rnb_err_t +RNBRemote::HandlePacket_DeallocateMemory (const char *p) +{ + StringExtractor packet (p); + packet.SetFilePos(2); // Skip the "_m" + nub_addr_t addr = packet.GetHexMaxU64 (StringExtractor::BigEndian, INVALID_NUB_ADDRESS); + + if (addr != INVALID_NUB_ADDRESS) + { + if (DNBProcessMemoryDeallocate (m_ctx.ProcessID(), addr)) + return SendPacket ("OK"); + } + return SendPacket ("E54"); +} + +/* + vAttach;pid + + Attach to a new process with the specified process ID. pid is a hexadecimal integer + identifying the process. If the stub is currently controlling a process, it is + killed. The attached process is stopped.This packet is only available in extended + mode (see extended mode). + + Reply: + "ENN" for an error + "Any Stop Reply Packet" for success + */ + +rnb_err_t +RNBRemote::HandlePacket_v (const char *p) +{ + if (strcmp (p, "vCont;c") == 0) + { + // Simple continue + return RNBRemote::HandlePacket_c("c"); + } + else if (strcmp (p, "vCont;s") == 0) + { + // Simple step + return RNBRemote::HandlePacket_s("s"); + } + else if (strstr (p, "vCont") == p) + { + rnb_err_t rnb_err = rnb_success; + typedef struct + { + nub_thread_t tid; + char action; + int signal; + } vcont_action_t; + + DNBThreadResumeActions thread_actions; + char *c = (char *)(p += strlen("vCont")); + char *c_end = c + strlen(c); + if (*c == '?') + return SendPacket ("vCont;c;C;s;S"); + + while (c < c_end && *c == ';') + { + ++c; // Skip the semi-colon + DNBThreadResumeAction thread_action; + thread_action.tid = INVALID_NUB_THREAD; + thread_action.state = eStateInvalid; + thread_action.signal = 0; + thread_action.addr = INVALID_NUB_ADDRESS; + + char action = *c++; + + switch (action) + { + case 'C': + errno = 0; + thread_action.signal = strtoul (c, &c, 16); + if (errno != 0) + return HandlePacket_ILLFORMED ("Could not parse signal in vCont packet"); + // Fall through to next case... + + case 'c': + // Continue + thread_action.state = eStateRunning; + break; + + case 'S': + errno = 0; + thread_action.signal = strtoul (c, &c, 16); + if (errno != 0) + return HandlePacket_ILLFORMED ("Could not parse signal in vCont packet"); + // Fall through to next case... + + case 's': + // Step + thread_action.state = eStateStepping; + break; + + break; + + default: + rnb_err = HandlePacket_ILLFORMED ("Unsupported action in vCont packet"); + break; + } + if (*c == ':') + { + errno = 0; + thread_action.tid = strtoul (++c, &c, 16); + if (errno != 0) + return HandlePacket_ILLFORMED ("Could not parse thread number in vCont packet"); + } + + thread_actions.Append (thread_action); + } + + // If a default action for all other threads wasn't mentioned + // then we should stop the threads + thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0); + DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst (), thread_actions.GetSize()); + return rnb_success; + } + else if (strstr (p, "vAttach") == p) + { + nub_process_t attach_pid = INVALID_NUB_PROCESS; + char err_str[1024]={'\0'}; + if (strstr (p, "vAttachWait;") == p) + { + p += strlen("vAttachWait;"); + std::string attach_name; + while (*p != '\0') + { + char smallbuf[3]; + smallbuf[0] = *p; + smallbuf[1] = *(p + 1); + smallbuf[2] = '\0'; + + errno = 0; + int ch = strtoul (smallbuf, NULL, 16); + if (errno != 0 && ch == 0) + { + return HandlePacket_ILLFORMED ("non-hex char in arg on 'vAttachWait' pkt"); + } + + attach_name.push_back(ch); + p += 2; + } + + attach_pid = DNBProcessAttachWait(attach_name.c_str (), m_ctx.LaunchFlavor(), NULL, 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback); + + } + else if (strstr (p, "vAttach;") == p) + { + p += strlen("vAttach;"); + char *end = NULL; + attach_pid = strtoul (p, &end, 16); // PID will be in hex, so use base 16 to decode + if (p != end && *end == '\0') + { + // Wait at most 30 second for attach + struct timespec attach_timeout_abstime; + DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0); + attach_pid = DNBProcessAttach(attach_pid, &attach_timeout_abstime, err_str, sizeof(err_str)); + } + } + else + return HandlePacket_UNIMPLEMENTED(p); + + + if (attach_pid != INVALID_NUB_PROCESS) + { + if (m_ctx.ProcessID() != attach_pid) + m_ctx.SetProcessID(attach_pid); + // Send a stop reply packet to indicate we successfully attached! + NotifyThatProcessStopped (); + return rnb_success; + } + else + { + m_ctx.LaunchStatus().SetError(-1, DNBError::Generic); + if (err_str[0]) + m_ctx.LaunchStatus().SetErrorString(err_str); + else + m_ctx.LaunchStatus().SetErrorString("attach failed"); + return SendPacket ("E01"); // E01 is our magic error value for attach failed. + } + } + + // All other failures come through here + return HandlePacket_UNIMPLEMENTED(p); +} + +/* `T XX' -- status of thread + Check if the specified thread is alive. + The thread number is in hex? */ + +rnb_err_t +RNBRemote::HandlePacket_T (const char *p) +{ + p++; + if (p == NULL || *p == '\0') + { + return HandlePacket_ILLFORMED ("No thread specified in T packet"); + } + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E15"); + } + errno = 0; + nub_thread_t tid = strtoul (p, NULL, 16); + if (errno != 0 && tid == 0) + { + return HandlePacket_ILLFORMED ("Could not parse thread number in T packet"); + } + + nub_state_t state = DNBThreadGetState (m_ctx.ProcessID(), tid); + if (state == eStateInvalid || state == eStateExited || state == eStateCrashed) + { + return SendPacket ("E16"); + } + + return SendPacket ("OK"); +} + + +rnb_err_t +RNBRemote::HandlePacket_z (const char *p) +{ + if (p == NULL || *p == '\0') + return HandlePacket_ILLFORMED ("No thread specified in z packet"); + + if (!m_ctx.HasValidProcessID()) + return SendPacket ("E15"); + + char packet_cmd = *p++; + char break_type = *p++; + + if (*p++ != ',') + return HandlePacket_ILLFORMED ("Comma separator missing in z packet"); + + char *c = NULL; + nub_process_t pid = m_ctx.ProcessID(); + errno = 0; + nub_addr_t addr = strtoull (p, &c, 16); + if (errno != 0 && addr == 0) + return HandlePacket_ILLFORMED ("Invalid address in z packet"); + p = c; + if (*p++ != ',') + return HandlePacket_ILLFORMED ("Comma separator missing in z packet"); + + errno = 0; + uint32_t byte_size = strtoul (p, &c, 16); + if (errno != 0 && byte_size == 0) + return HandlePacket_ILLFORMED ("Invalid length in z packet"); + + if (packet_cmd == 'Z') + { + // set + switch (break_type) + { + case '0': // set software breakpoint + case '1': // set hardware breakpoint + { + // gdb can send multiple Z packets for the same address and + // these calls must be ref counted. + bool hardware = (break_type == '1'); + + // Check if we currently have a breakpoint already set at this address + BreakpointMapIter pos = m_breakpoints.find(addr); + if (pos != m_breakpoints.end()) + { + // We do already have a breakpoint at this address, increment + // its reference count and return OK + pos->second.Retain(); + return SendPacket ("OK"); + } + else + { + // We do NOT already have a breakpoint at this address, So lets + // create one. + nub_break_t break_id = DNBBreakpointSet (pid, addr, byte_size, hardware); + if (break_id != INVALID_NUB_BREAK_ID) + { + // We successfully created a breakpoint, now lets full out + // a ref count structure with the breakID and add it to our + // map. + Breakpoint rnbBreakpoint(break_id); + m_breakpoints[addr] = rnbBreakpoint; + return SendPacket ("OK"); + } + else + { + // We failed to set the software breakpoint + return SendPacket ("E09"); + } + } + } + break; + + case '2': // set write watchpoint + case '3': // set read watchpoint + case '4': // set access watchpoint + { + bool hardware = true; + uint32_t watch_flags = 0; + if (break_type == '2') + watch_flags = WATCH_TYPE_WRITE; + else if (break_type == '3') + watch_flags = WATCH_TYPE_READ; + else + watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE; + + // Check if we currently have a watchpoint already set at this address + BreakpointMapIter pos = m_watchpoints.find(addr); + if (pos != m_watchpoints.end()) + { + // We do already have a watchpoint at this address, increment + // its reference count and return OK + pos->second.Retain(); + return SendPacket ("OK"); + } + else + { + // We do NOT already have a breakpoint at this address, So lets + // create one. + nub_watch_t watch_id = DNBWatchpointSet (pid, addr, byte_size, watch_flags, hardware); + if (watch_id != INVALID_NUB_BREAK_ID) + { + // We successfully created a watchpoint, now lets full out + // a ref count structure with the watch_id and add it to our + // map. + Breakpoint rnbWatchpoint(watch_id); + m_watchpoints[addr] = rnbWatchpoint; + return SendPacket ("OK"); + } + else + { + // We failed to set the watchpoint + return SendPacket ("E09"); + } + } + } + break; + + default: + break; + } + } + else if (packet_cmd == 'z') + { + // remove + switch (break_type) + { + case '0': // remove software breakpoint + case '1': // remove hardware breakpoint + { + // gdb can send multiple z packets for the same address and + // these calls must be ref counted. + BreakpointMapIter pos = m_breakpoints.find(addr); + if (pos != m_breakpoints.end()) + { + // We currently have a breakpoint at address ADDR. Decrement + // its reference count, and it that count is now zero we + // can clear the breakpoint. + pos->second.Release(); + if (pos->second.RefCount() == 0) + { + if (DNBBreakpointClear (pid, pos->second.BreakID())) + { + m_breakpoints.erase(pos); + return SendPacket ("OK"); + } + else + { + return SendPacket ("E08"); + } + } + else + { + // We still have references to this breakpoint don't + // delete it, just decrementing the reference count + // is enough. + return SendPacket ("OK"); + } + } + else + { + // We don't know about any breakpoints at this address + return SendPacket ("E08"); + } + } + break; + + case '2': // remove write watchpoint + case '3': // remove read watchpoint + case '4': // remove access watchpoint + { + // gdb can send multiple z packets for the same address and + // these calls must be ref counted. + BreakpointMapIter pos = m_watchpoints.find(addr); + if (pos != m_watchpoints.end()) + { + // We currently have a watchpoint at address ADDR. Decrement + // its reference count, and it that count is now zero we + // can clear the watchpoint. + pos->second.Release(); + if (pos->second.RefCount() == 0) + { + if (DNBWatchpointClear (pid, pos->second.BreakID())) + { + m_watchpoints.erase(pos); + return SendPacket ("OK"); + } + else + { + return SendPacket ("E08"); + } + } + else + { + // We still have references to this watchpoint don't + // delete it, just decrementing the reference count + // is enough. + return SendPacket ("OK"); + } + } + else + { + // We don't know about any watchpoints at this address + return SendPacket ("E08"); + } + } + break; + + default: + break; + } + } + return HandlePacket_UNIMPLEMENTED(p); +} + +/* `p XX' + print the contents of register X */ + +rnb_err_t +RNBRemote::HandlePacket_p (const char *p) +{ + if (p == NULL || *p == '\0') + { + return HandlePacket_ILLFORMED ("No thread specified in p packet"); + } + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E15"); + } + nub_process_t pid = m_ctx.ProcessID(); + errno = 0; + uint32_t reg = strtoul (p + 1, NULL, 16); + if (errno != 0 && reg == 0) + { + return HandlePacket_ILLFORMED ("Could not parse thread number in p packet"); + } + + const register_map_entry_t *reg_entry; + + if (reg < g_num_reg_entries) + reg_entry = &g_reg_entries[reg]; + else + reg_entry = NULL; + + std::ostringstream ostrm; + if (reg_entry == NULL) + { + DNBLogError("RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n", p, reg); + ostrm << "00000000"; + } + else if (reg_entry->nub_info.reg == -1) + { + if (reg_entry->gdb_size > 0) + { + if (reg_entry->fail_value != NULL) + { + append_hex_value(ostrm, reg_entry->fail_value, reg_entry->gdb_size, false); + } + else + { + std::basic_string zeros(reg_entry->gdb_size, '\0'); + append_hex_value(ostrm, zeros.data(), zeros.size(), false); + } + } + } + else + { + nub_thread_t tid = GetCurrentThread(); + register_value_in_hex_fixed_width (ostrm, pid, tid, reg_entry); + } + return SendPacket (ostrm.str()); +} + +/* `Pnn=rrrrr' + Set register number n to value r. + n and r are hex strings. */ + +rnb_err_t +RNBRemote::HandlePacket_P (const char *p) +{ + if (p == NULL || *p == '\0') + { + return HandlePacket_ILLFORMED ("Empty P packet"); + } + if (!m_ctx.HasValidProcessID()) + { + return SendPacket ("E28"); + } + + nub_process_t pid = m_ctx.ProcessID(); + + StringExtractor packet (p); + + const char cmd_char = packet.GetChar(); + // Register ID is always in big endian + const uint32_t reg = packet.GetHexMaxU32 (false, UINT32_MAX); + const char equal_char = packet.GetChar(); + + if (cmd_char != 'P') + return HandlePacket_ILLFORMED ("Improperly formed P packet"); + + if (reg == UINT32_MAX) + return SendPacket ("E29"); + + if (equal_char != '=') + return SendPacket ("E30"); + + const register_map_entry_t *reg_entry; + + if (reg >= g_num_reg_entries) + return SendPacket("E47"); + + reg_entry = &g_reg_entries[reg]; + + if (reg_entry->nub_info.set == -1 && reg_entry->nub_info.reg == -1) + { + DNBLogError("RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n", p, reg); + return SendPacket("E48"); + } + + DNBRegisterValue reg_value; + reg_value.info = reg_entry->nub_info; + packet.GetHexBytes (reg_value.value.v_sint8, reg_entry->gdb_size, 0xcc); + + nub_thread_t tid; + tid = GetCurrentThread (); + + if (!DNBThreadSetRegisterValueByID (pid, tid, reg_entry->nub_info.set, reg_entry->nub_info.reg, ®_value)) + { + return SendPacket ("E32"); + } + return SendPacket ("OK"); +} + +/* `c [addr]' + Continue, optionally from a specified address. */ + +rnb_err_t +RNBRemote::HandlePacket_c (const char *p) +{ + const nub_process_t pid = m_ctx.ProcessID(); + + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("E23"); + + DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS }; + + if (*(p + 1) != '\0') + { + action.tid = GetContinueThread(); + errno = 0; + action.addr = strtoull (p + 1, NULL, 16); + if (errno != 0 && action.addr == 0) + return HandlePacket_ILLFORMED ("Could not parse address in c packet"); + } + + DNBThreadResumeActions thread_actions; + thread_actions.Append(action); + thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0); + if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize())) + return SendPacket ("E25"); + // Don't send an "OK" packet; response is the stopped/exited message. + return rnb_success; +} + +/* `C sig [;addr]' + Resume with signal sig, optionally at address addr. */ + +rnb_err_t +RNBRemote::HandlePacket_C (const char *p) +{ + const nub_process_t pid = m_ctx.ProcessID(); + + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("E36"); + + DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateRunning, 0, INVALID_NUB_ADDRESS }; + int process_signo = -1; + if (*(p + 1) != '\0') + { + action.tid = GetContinueThread(); + char *end = NULL; + errno = 0; + process_signo = strtoul (p + 1, &end, 16); + if (errno != 0) + return HandlePacket_ILLFORMED ("Could not parse signal in C packet"); + else if (*end == ';') + { + errno = 0; + action.addr = strtoull (end + 1, NULL, 16); + if (errno != 0 && action.addr == 0) + return HandlePacket_ILLFORMED ("Could not parse address in C packet"); + } + } + + DNBThreadResumeActions thread_actions; + thread_actions.Append (action); + thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, action.signal); + if (!DNBProcessSignal(pid, process_signo)) + return SendPacket ("E52"); + if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize())) + return SendPacket ("E38"); + /* Don't send an "OK" packet; response is the stopped/exited message. */ + return rnb_success; +} + +//---------------------------------------------------------------------- +// 'D' packet +// Detach from gdb. +//---------------------------------------------------------------------- +rnb_err_t +RNBRemote::HandlePacket_D (const char *p) +{ + SendPacket ("OK"); + if (m_ctx.HasValidProcessID()) + DNBProcessDetach(m_ctx.ProcessID()); + return rnb_success; +} + +/* `k' + Kill the inferior process. */ + +rnb_err_t +RNBRemote::HandlePacket_k (const char *p) +{ + if (!m_ctx.HasValidProcessID()) + return SendPacket ("E26"); + if (!DNBProcessKill (m_ctx.ProcessID())) + return SendPacket ("E27"); + return SendPacket ("OK"); +} + +rnb_err_t +RNBRemote::HandlePacket_stop_process (const char *p) +{ + DNBProcessSignal (m_ctx.ProcessID(), SIGSTOP); + //DNBProcessSignal (m_ctx.ProcessID(), SIGINT); + // Do not send any response packet! Wait for the stop reply packet to naturally happen + return rnb_success; +} + +/* `s' + Step the inferior process. */ + +rnb_err_t +RNBRemote::HandlePacket_s (const char *p) +{ + const nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("E32"); + + // Hardware supported stepping not supported on arm + nub_thread_t tid = GetContinueThread (); + if (tid == 0 || tid == -1) + tid = GetCurrentThread(); + + if (tid == INVALID_NUB_THREAD) + return SendPacket ("E33"); + + DNBThreadResumeActions thread_actions; + thread_actions.AppendAction(tid, eStateStepping); + + // Make all other threads stop when we are stepping + thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0); + if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize())) + return SendPacket ("E49"); + // Don't send an "OK" packet; response is the stopped/exited message. + return rnb_success; +} + +/* `S sig [;addr]' + Step with signal sig, optionally at address addr. */ + +rnb_err_t +RNBRemote::HandlePacket_S (const char *p) +{ + const nub_process_t pid = m_ctx.ProcessID(); + if (pid == INVALID_NUB_PROCESS) + return SendPacket ("E36"); + + DNBThreadResumeAction action = { INVALID_NUB_THREAD, eStateStepping, 0, INVALID_NUB_ADDRESS }; + + if (*(p + 1) != '\0') + { + char *end = NULL; + errno = 0; + action.signal = strtoul (p + 1, &end, 16); + if (errno != 0) + return HandlePacket_ILLFORMED ("Could not parse signal in S packet"); + else if (*end == ';') + { + errno = 0; + action.addr = strtoull (end + 1, NULL, 16); + if (errno != 0 && action.addr == 0) + { + return HandlePacket_ILLFORMED ("Could not parse address in S packet"); + } + } + } + + action.tid = GetContinueThread (); + if (action.tid == 0 || action.tid == -1) + return SendPacket ("E40"); + + nub_state_t tstate = DNBThreadGetState (pid, action.tid); + if (tstate == eStateInvalid || tstate == eStateExited) + return SendPacket ("E37"); + + + DNBThreadResumeActions thread_actions; + thread_actions.Append (action); + + // Make all other threads stop when we are stepping + thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); + if (!DNBProcessResume (pid, thread_actions.GetFirst(), thread_actions.GetSize())) + return SendPacket ("E39"); + + // Don't send an "OK" packet; response is the stopped/exited message. + return rnb_success; +} + +rnb_err_t +RNBRemote::HandlePacket_qHostInfo (const char *p) +{ + std::ostringstream strm; + + uint32_t cputype, is_64_bit_capable; + size_t len = sizeof(cputype); + bool promoted_to_64 = false; + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof (is_64_bit_capable); + if (::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0) == 0) + { + if (is_64_bit_capable && ((cputype & CPU_ARCH_ABI64) == 0)) + { + promoted_to_64 = true; + cputype |= CPU_ARCH_ABI64; + } + } + + strm << "cputype:" << std::dec << cputype << ';'; + } + + uint32_t cpusubtype; + len = sizeof(cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + if (promoted_to_64 && + cputype == CPU_TYPE_X86_64 && + cpusubtype == CPU_SUBTYPE_486) + cpusubtype = CPU_SUBTYPE_X86_64_ALL; + + strm << "cpusubtype:" << std::dec << cpusubtype << ';'; + } + + char ostype[64]; + len = sizeof(ostype); + if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0) + strm << "ostype:" << std::dec << ostype << ';'; + + strm << "vendor:apple;"; + +#if defined (__LITTLE_ENDIAN__) + strm << "endian:little;"; +#elif defined (__BIG_ENDIAN__) + strm << "endian:big;"; +#elif defined (__PDP_ENDIAN__) + strm << "endian:pdp;"; +#endif + + strm << "ptrsize:" << std::dec << sizeof(void *) << ';'; + return SendPacket (strm.str()); +} + diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h new file mode 100644 index 000000000000..bec9d1812d0e --- /dev/null +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -0,0 +1,309 @@ +//===-- RNBRemote.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBRemote_h__ +#define __RNBRemote_h__ + +#include "RNBDefs.h" +#include "DNB.h" +#include "RNBContext.h" +#include "RNBSocket.h" +#include "PThreadMutex.h" +#include +#include +#include +#include + +class RNBSocket; +class RNBContext; +class PThreadEvents; + +enum event_loop_mode { debug_nub, gdb_remote_protocol, done }; + +class RNBRemote +{ +public: + + typedef enum { + invalid_packet = 0, + ack, // '+' + nack, // '-' + halt, // ^C (async halt) + use_extended_mode, // '!' + why_halted, // '?' + set_argv, // 'A' + set_bp, // 'B' + cont, // 'c' + continue_with_sig, // 'C' + detach, // 'D' + read_general_regs, // 'g' + write_general_regs, // 'G' + set_thread, // 'H' + step_inferior_one_cycle, // 'i' + signal_and_step_inf_one_cycle, // 'I' + kill, // 'k' + read_memory, // 'm' + write_memory, // 'M' + read_register, // 'p' + write_register, // 'P' + restart, // 'R' + single_step, // 's' + single_step_with_sig, // 'S' + search_mem_backwards, // 't' + thread_alive_p, // 'T' + vattach, // 'vAttach' + vattachwait, // 'vAttachWait' + vcont, // 'vCont' + vcont_list_actions, // 'vCont?' + write_data_to_memory, // 'X' + insert_mem_bp, // 'Z0' + remove_mem_bp, // 'z0' + insert_hardware_bp, // 'Z1' + remove_hardware_bp, // 'z1' + insert_write_watch_bp, // 'Z2' + remove_write_watch_bp, // 'z2' + insert_read_watch_bp, // 'Z3' + remove_read_watch_bp, // 'z3' + insert_access_watch_bp, // 'Z4' + remove_access_watch_bp, // 'z4' + + query_current_thread_id, // 'qC' + query_memory_crc, // 'qCRC:' + query_thread_ids_first, // 'qfThreadInfo' + query_thread_ids_subsequent, // 'qsThreadInfo' + query_thread_extra_info, // 'qThreadExtraInfo' + query_thread_stop_info, // 'qThreadStopInfo' + query_image_offsets, // 'qOffsets' + query_symbol_lookup, // 'gSymbols' + query_launch_success, // 'qLaunchSuccess' + query_register_info, // 'qRegisterInfo' + query_shlib_notify_info_addr, // 'qShlibInfoAddr' + query_step_packet_supported, // 'qStepPacketSupported' + query_host_info, // 'qHostInfo' + pass_signals_to_inferior, // 'QPassSignals' + start_noack_mode, // 'QStartNoAckMode' + set_logging_mode, // 'QSetLogging:' + set_max_packet_size, // 'QSetMaxPacketSize:' + set_max_payload_size, // 'QSetMaxPayloadSize:' + set_environment_variable, // 'QEnvironment:' + allocate_memory, // '_M' + deallocate_memory, // '_m' + + unknown_type, + } PacketEnum; + + typedef rnb_err_t (RNBRemote::*HandlePacketCallback)(const char *p); + + RNBRemote(bool use_native_regs); + ~RNBRemote(); + + static void InitializeRegisters (int use_native); + + rnb_err_t HandleAsyncPacket(PacketEnum *type = NULL); + rnb_err_t HandleReceivedPacket(PacketEnum *type = NULL); + + nub_thread_t GetContinueThread () const + { + return m_continue_thread; + } + + void SetContinueThread (nub_thread_t tid) + { + m_continue_thread = tid; + } + + nub_thread_t GetCurrentThread () const + { + if (m_thread == 0 || m_thread == -1) + return DNBProcessGetCurrentThread (m_ctx.ProcessID()); + return m_thread; + } + + void SetCurrentThread (nub_thread_t tid) + { + DNBProcessSetCurrentThread (m_ctx.ProcessID(), tid); + m_thread = tid; + } + + static void* ThreadFunctionReadRemoteData(void *arg); + void StartReadRemoteDataThread (); + void StopReadRemoteDataThread (); + + void NotifyThatProcessStopped (void); + + rnb_err_t HandlePacket_A (const char *p); + rnb_err_t HandlePacket_H (const char *p); + rnb_err_t HandlePacket_qC (const char *p); + rnb_err_t HandlePacket_qLaunchSuccess (const char *p); + rnb_err_t HandlePacket_qRegisterInfo (const char *p); + rnb_err_t HandlePacket_qShlibInfoAddr (const char *p); + rnb_err_t HandlePacket_qStepPacketSupported (const char *p); + rnb_err_t HandlePacket_qThreadInfo (const char *p); + rnb_err_t HandlePacket_qThreadExtraInfo (const char *p); + rnb_err_t HandlePacket_qThreadStopInfo (const char *p); + rnb_err_t HandlePacket_qHostInfo (const char *p); + rnb_err_t HandlePacket_Q (const char *p); + rnb_err_t HandlePacket_last_signal (const char *p); + rnb_err_t HandlePacket_m (const char *p); + rnb_err_t HandlePacket_M (const char *p); + rnb_err_t HandlePacket_X (const char *p); + rnb_err_t HandlePacket_g (const char *p); + rnb_err_t HandlePacket_G (const char *p); + rnb_err_t HandlePacket_z (const char *p); + rnb_err_t HandlePacket_T (const char *p); + rnb_err_t HandlePacket_p (const char *p); + rnb_err_t HandlePacket_P (const char *p); + rnb_err_t HandlePacket_c (const char *p); + rnb_err_t HandlePacket_C (const char *p); + rnb_err_t HandlePacket_D (const char *p); + rnb_err_t HandlePacket_k (const char *p); + rnb_err_t HandlePacket_s (const char *p); + rnb_err_t HandlePacket_S (const char *p); + rnb_err_t HandlePacket_v (const char *p); + rnb_err_t HandlePacket_UNIMPLEMENTED (const char *p); + rnb_err_t HandlePacket_ILLFORMED (const char *description); + rnb_err_t HandlePacket_AllocateMemory (const char *p); + rnb_err_t HandlePacket_DeallocateMemory (const char *p); + + rnb_err_t HandlePacket_stop_process (const char *p); + + rnb_err_t SendStopReplyPacketForThread (nub_thread_t tid); + rnb_err_t SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer); + rnb_err_t SendSTDOUTPacket (char *buf, nub_size_t buf_size); + rnb_err_t SendSTDERRPacket (char *buf, nub_size_t buf_size); + void FlushSTDIO (); + + RNBContext& Context() { return m_ctx; } + RNBSocket& Comm() { return m_comm; } + +private: + // Outlaw some contructors + RNBRemote (const RNBRemote &); + +protected: + + static void + InitializeNativeRegisters (); + + rnb_err_t GetCommData (); + void CommDataReceived(const std::string& data); + struct Packet + { + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + PacketEnum type; + HandlePacketCallback normal; // Function to call when inferior is halted + HandlePacketCallback async; // Function to call when inferior is running + std::string abbrev; + std::string printable_name; + Packet() : + type(invalid_packet), + normal (NULL), + async (NULL), + abbrev (), + printable_name () + { + } + + Packet( PacketEnum in_type, + HandlePacketCallback in_normal, + HandlePacketCallback in_async, + const char *in_abbrev, + const char *in_printable_name) : + type (in_type), + normal (in_normal), + async (in_async), + abbrev (in_abbrev), + printable_name (in_printable_name) + { + } + }; + + rnb_err_t GetPacket (std::string &packet_data, RNBRemote::Packet& packet_info, bool wait); + rnb_err_t SendPacket (const std::string &); + + void CreatePacketTable (); + rnb_err_t GetPacketPayload (std::string &); + + // gdb can send multiple Z/z packets for the same address and + // these calls must be ref counted. + typedef struct Breakpoint + { + Breakpoint(nub_break_t breakID) : + m_breakID(breakID), + m_refCount(1) + { + } + + Breakpoint() : + m_breakID(INVALID_NUB_BREAK_ID), + m_refCount(0) + { + } + + Breakpoint(const Breakpoint& rhs) : + m_breakID(rhs.m_breakID), + m_refCount(rhs.m_refCount) + { + } + + nub_break_t BreakID() const { return m_breakID; } + uint32_t RefCount() const { return m_refCount; } + void Release() { if (m_refCount > 0) --m_refCount; } + void Retain() { ++m_refCount; } + + nub_break_t m_breakID; + uint32_t m_refCount; + }; + typedef std::map BreakpointMap; + typedef BreakpointMap::iterator BreakpointMapIter; + typedef BreakpointMap::const_iterator BreakpointMapConstIter; + RNBContext m_ctx; // process context + RNBSocket m_comm; // communication port + bool m_extended_mode; // are we in extended mode? + bool m_noack_mode; // are we in no-ack mode? + nub_thread_t m_continue_thread; // thread to continue; 0 for any, -1 for all + nub_thread_t m_thread; // thread for other ops; 0 for any, -1 for all + PThreadMutex m_mutex; // Mutex that protects + uint32_t m_packets_recvd; + Packet::collection m_packets; + std::deque m_rx_packets; + std::string m_rx_partial_data; // For packets that may come in more than one batch, anything left over can be left here + pthread_t m_rx_pthread; + BreakpointMap m_breakpoints; + BreakpointMap m_watchpoints; + uint32_t m_max_payload_size; // the maximum sized payload we should send to gdb + bool m_use_native_regs; +}; + +/* We translate the /usr/include/mach/exception_types.h exception types + (e.g. EXC_BAD_ACCESS) to the fake BSD signal numbers that gdb uses + in include/gdb/signals.h (e.g. TARGET_EXC_BAD_ACCESS). These hard + coded values for TARGET_EXC_BAD_ACCESS et al must match the gdb + values in its include/gdb/signals.h. */ + +#define TARGET_EXC_BAD_ACCESS 0x91 +#define TARGET_EXC_BAD_INSTRUCTION 0x92 +#define TARGET_EXC_ARITHMETIC 0x93 +#define TARGET_EXC_EMULATION 0x94 +#define TARGET_EXC_SOFTWARE 0x95 +#define TARGET_EXC_BREAKPOINT 0x96 + +/* Generally speaking, you can't assume gdb can receive more than 399 bytes + at a time with a random gdb. This bufsize constant is only specifying + how many bytes gdb can *receive* from debugserver -- it tells us nothing + about how many bytes gdb might try to send in a single packet. */ +#define DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE 399 + +#endif // #ifndef __RNBRemote_h__ diff --git a/lldb/tools/debugserver/source/RNBServices.cpp b/lldb/tools/debugserver/source/RNBServices.cpp new file mode 100644 index 000000000000..1fe791d37be7 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBServices.cpp @@ -0,0 +1,145 @@ +//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Christopher Friesen on 3/21/08. +// +//===----------------------------------------------------------------------===// + +#import "RNBServices.h" + +#import +#import +#import "DNBLog.h" + +#if defined (__arm__) +#import +#endif + +int +ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable) +{ +#if defined (__arm__) + int result = -1; + + CFAllocatorRef alloc = kCFAllocatorDefault; + + // Create a mutable array that we can populate. Specify zero so it can be of any size. + CFReleaser plistMutableArray (::CFArrayCreateMutable (alloc, 0, &kCFTypeArrayCallBacks)); + + CFReleaser sbsFrontAppID (::SBSCopyFrontmostApplicationDisplayIdentifier ()); + CFReleaser sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); + + CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); + CFIndex i = 0; + for (i = 0; i < count; i++) + { + CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); + + // Create a new mutable dictionary for each application + CFReleaser appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + + // Get the process id for the app (if there is one) + pid_t pid = INVALID_NUB_PROCESS; + if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &pid) == true) + { + CFReleaser pidCFNumber (::CFNumberCreate (alloc, kCFNumberSInt32Type, &pid)); + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get()); + } + + // Set the a boolean to indicate if this is the front most + if (sbsFrontAppID.get() && displayIdentifier && (::CFStringCompare (sbsFrontAppID.get(), displayIdentifier, 0) == kCFCompareEqualTo)) + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanTrue); + else + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse); + + + CFReleaser executablePath (::SBSCopyExecutablePathForDisplayIdentifier (displayIdentifier)); + if (executablePath.get() != NULL) + { + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, executablePath.get()); + } + + CFReleaser iconImagePath (::SBSCopyIconImagePathForDisplayIdentifier (displayIdentifier)) ; + if (iconImagePath.get() != NULL) + { + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, iconImagePath.get()); + } + + CFReleaser localizedDisplayName (::SBSCopyLocalizedApplicationNameForDisplayIdentifier (displayIdentifier)); + if (localizedDisplayName.get() != NULL) + { + ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, localizedDisplayName.get()); + } + + // Append the application info to the plist array + ::CFArrayAppendValue (plistMutableArray.get(), appInfoDict.get()); + } + + CFReleaser plistData (::CFPropertyListCreateXMLData (alloc, plistMutableArray.get())); + + // write plist to service port + if (plistData.get() != NULL) + { + CFIndex size = ::CFDataGetLength (plistData.get()); + const UInt8 *bytes = ::CFDataGetBytePtr (plistData.get()); + if (bytes != NULL && size > 0) + { + plist.assign((char *)bytes, size); + return 0; // Success + } + else + { + DNBLogError("empty application property list."); + result = -2; + } + } + else + { + DNBLogError("serializing task list."); + result = -3; + } + + return result; +#else + // TODO: list all current processes + DNBLogError("SBS doesn't support getting application list."); + return -1; +#endif +} + + +bool +IsSBProcess (nub_process_t pid) +{ +#if defined (__arm__) + bool opt_runningApps = true; + bool opt_debuggable = false; + + CFReleaser sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); + if (sbsAppIDs.get() != NULL) + { + CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); + CFIndex i = 0; + for (i = 0; i < count; i++) + { + CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); + + // Get the process id for the app (if there is one) + pid_t sbs_pid = INVALID_NUB_PROCESS; + if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) + { + if (sbs_pid == pid) + return true; + } + } + } +#endif + return false; +} + diff --git a/lldb/tools/debugserver/source/RNBServices.h b/lldb/tools/debugserver/source/RNBServices.h new file mode 100644 index 000000000000..0164e33b0bf5 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBServices.h @@ -0,0 +1,29 @@ +//===-- RNBServices.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Christopher Friesen on 3/21/08. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBServices_h__ +#define __RNBServices_h__ + +#include +#include "RNBDefs.h" + +#define DTSERVICES_APP_FRONTMOST_KEY CFSTR("isFrontApp") +#define DTSERVICES_APP_PATH_KEY CFSTR("executablePath") +#define DTSERVICES_APP_ICON_PATH_KEY CFSTR("iconPath") +#define DTSERVICES_APP_DISPLAY_NAME_KEY CFSTR("displayName") +#define DTSERVICES_APP_PID_KEY CFSTR("pid") + +int ListApplications (std::string &plist, bool opt_runningApps, bool opt_debuggable); +bool IsSBProcess (nub_process_t pid); + +#endif // __RNBServices_h__ diff --git a/lldb/tools/debugserver/source/RNBSocket.cpp b/lldb/tools/debugserver/source/RNBSocket.cpp new file mode 100644 index 000000000000..08fa4ac79ce2 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBSocket.cpp @@ -0,0 +1,251 @@ +//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#include "RNBSocket.h" +#include +#include +#include +#include +#include +#include "DNBLog.h" +#include "DNBError.h" + +#if defined (__arm__) +#include "lockdown.h" +#endif + +/* Once we have a RNBSocket object with a port # specified, + this function is called to wait for an incoming connection. + This function blocks while waiting for that connection. */ + +rnb_err_t +RNBSocket::Listen (in_port_t listen_port_num) +{ + //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); + // Disconnect without saving errno + Disconnect (false); + + DNBError err; + int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_port == -1) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_port); + + if (err.Fail()) + return rnb_err; + + // enable local address reuse + SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof sa); + sa.sin_len = sizeof sa; + sa.sin_family = AF_INET; + sa.sin_port = htons (listen_port_num); + sa.sin_addr.s_addr = htonl (INADDR_ANY); + + int error = ::bind (listen_port, (struct sockaddr *) &sa, sizeof(sa)); + if (error == -1) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_port); + + if (err.Fail()) + { + ClosePort (listen_port, false); + return rnb_err; + } + + error = ::listen (listen_port, 1); + if (error == -1) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_port); + + if (err.Fail()) + { + ClosePort (listen_port, false); + return rnb_err; + } + + m_conn_port = ::accept (listen_port, NULL, 0); + if (m_conn_port == -1) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::accept ( socket = %i, address = NULL, address_len = 0 )", listen_port); + + if (err.Fail()) + { + ClosePort (listen_port, false); + return rnb_err; + } + else + { + // We are done with the listen port + ClosePort (listen_port, false); + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_conn_port, IPPROTO_TCP, TCP_NODELAY, 1); + } + + return rnb_success; +} + +#if defined (__arm__) +rnb_err_t +RNBSocket::ConnectToService() +{ + DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME); + // Disconnect from any previous connections + Disconnect(false); + + m_conn_port = ::lockdown_checkin (NULL, NULL); + if (m_conn_port == -1) + { + DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_checkin(NULL, NULL) failed"); + return rnb_not_connected; + } + m_conn_port_from_lockdown = true; + return rnb_success; +} +#endif + +rnb_err_t +RNBSocket::OpenFile (const char *path) +{ + DNBError err; + m_conn_port = open (path, O_RDWR); + if (m_conn_port == -1) + { + err.SetError(errno, DNBError::POSIX); + err.LogThreaded ("can't open file '%s'", path); + return rnb_not_connected; + } + else + { + struct termios stdin_termios; + + if (::tcgetattr (m_conn_port, &stdin_termios) == 0) + { + stdin_termios.c_lflag &= ~ECHO; // Turn off echoing + stdin_termios.c_lflag &= ~ICANON; // Get one char at a time + ::tcsetattr (m_conn_port, TCSANOW, &stdin_termios); + } + } + return rnb_success; +} + +int +RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value) +{ + return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value)); +} + +rnb_err_t +RNBSocket::Disconnect (bool save_errno) +{ + if (m_conn_port_from_lockdown) + m_conn_port_from_lockdown = false; + return ClosePort (m_conn_port, save_errno); +} + + +rnb_err_t +RNBSocket::Read (std::string &p) +{ + char buf[1024]; + p.clear(); + + // Note that BUF is on the stack so we must be careful to keep any + // writes to BUF from overflowing or we'll have security issues. + + if (m_conn_port == -1) + return rnb_err; + + //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); + DNBError err; + int bytesread = read (m_conn_port, buf, sizeof (buf)); + if (bytesread <= 0) + err.SetError(errno, DNBError::POSIX); + else + p.append(buf, bytesread); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::read ( %i, %p, %zu ) => %i", m_conn_port, buf, sizeof (buf), bytesread); + + // Our port went away - we have to mark this so IsConnected will return the truth. + if (bytesread == 0) + { + m_conn_port = -1; + return rnb_not_connected; + } + else if (bytesread == -1) + { + m_conn_port = -1; + return rnb_err; + } + // Strip spaces from the end of the buffer + while (!p.empty() && isspace (p[p.size() - 1])) + p.erase (p.size () - 1); + + // Most data in the debugserver packets valid printable characters... + DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str()); + return rnb_success; +} + +rnb_err_t +RNBSocket::Write (const void *buffer, size_t length) +{ + if (m_conn_port == -1) + return rnb_err; + + DNBError err; + int bytessent = send (m_conn_port, buffer, length, 0); + if (bytessent < 0) + err.SetError(errno, DNBError::POSIX); + + if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) + err.LogThreaded("::send ( socket = %i, buffer = %p, length = %zu, flags = 0 ) => %i", m_conn_port, buffer, length, bytessent); + + if (bytessent < 0) + return rnb_err; + + if (bytessent != length) + return rnb_err; + + DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", length, (char *)buffer); // All data is string based in debugserver, so this is safe + DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", length, (char *)buffer); + + return rnb_success; +} + + +rnb_err_t +RNBSocket::ClosePort (int& fd, bool save_errno) +{ + int close_err = 0; + if (fd > 0) + { + errno = 0; + close_err = close (fd); + fd = -1; + } + return close_err != 0 ? rnb_err : rnb_success; +} + + diff --git a/lldb/tools/debugserver/source/RNBSocket.h b/lldb/tools/debugserver/source/RNBSocket.h new file mode 100644 index 000000000000..de3db806ded5 --- /dev/null +++ b/lldb/tools/debugserver/source/RNBSocket.h @@ -0,0 +1,65 @@ +//===-- RNBSocket.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 12/12/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __RNBSocket_h__ +#define __RNBSocket_h__ + +#include "RNBDefs.h" +#include +#include +#include +#include "DNBTimer.h" + +class RNBSocket +{ +public: + + RNBSocket () : + m_conn_port (-1), + m_conn_port_from_lockdown (false), + m_timer (true) // Make a thread safe timer + { + } + ~RNBSocket (void) + { + Disconnect (false); + } + + rnb_err_t Listen (in_port_t listen_port_num); +#if defined (__arm__) + rnb_err_t ConnectToService(); +#endif + rnb_err_t OpenFile (const char *path); + rnb_err_t Disconnect (bool save_errno); + rnb_err_t Read (std::string &p); + rnb_err_t Write (const void *buffer, size_t length); + + bool IsConnected () const { return m_conn_port != -1; } + void SaveErrno (int curr_errno); + DNBTimer& Timer() { return m_timer; } + + static int SetSocketOption(int fd, int level, int option_name, int option_value); +private: + // Outlaw some constructors + RNBSocket (const RNBSocket &); + +protected: + rnb_err_t ClosePort (int& fd, bool save_errno); + + int m_conn_port; // Socket we use to communicate once conn established + bool m_conn_port_from_lockdown; + DNBTimer m_timer; +}; + + +#endif // #ifndef __RNBSocket_h__ diff --git a/lldb/tools/debugserver/source/SysSignal.cpp b/lldb/tools/debugserver/source/SysSignal.cpp new file mode 100644 index 000000000000..69f34ed605c5 --- /dev/null +++ b/lldb/tools/debugserver/source/SysSignal.cpp @@ -0,0 +1,66 @@ +//===-- SysSignal.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#include "SysSignal.h" +#include +#include + +const char * +SysSignal::Name(int signal) +{ + switch (signal) + { + 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) + case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) +#else // !_POSIX_C_SOURCE + case SIGEMT: return "SIGEMT"; // 7 EMT instruction +#endif // !_POSIX_C_SOURCE + 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; +} diff --git a/lldb/tools/debugserver/source/SysSignal.h b/lldb/tools/debugserver/source/SysSignal.h new file mode 100644 index 000000000000..438d137f3104 --- /dev/null +++ b/lldb/tools/debugserver/source/SysSignal.h @@ -0,0 +1,23 @@ +//===-- SysSignal.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 6/18/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __SysSignal_h__ +#define __SysSignal_h__ + +class SysSignal +{ +public: + static const char *Name(int signal); +}; + +#endif diff --git a/lldb/tools/debugserver/source/TTYState.cpp b/lldb/tools/debugserver/source/TTYState.cpp new file mode 100644 index 000000000000..28bc956dc28b --- /dev/null +++ b/lldb/tools/debugserver/source/TTYState.cpp @@ -0,0 +1,122 @@ +//===-- TTYState.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/26/07. +// +//===----------------------------------------------------------------------===// + +#include "TTYState.h" +#include +#include +#include + +TTYState::TTYState() : + m_fd(-1), + m_tflags(-1), + m_ttystateErr(-1), + m_processGroup(-1) +{ +} + +TTYState::~TTYState() +{ +} + +bool +TTYState::GetTTYState (int fd, bool saveProcessGroup) +{ + if (fd >= 0 && ::isatty (fd)) + { + m_fd = fd; + m_tflags = fcntl (fd, F_GETFL, 0); + m_ttystateErr = tcgetattr (fd, &m_ttystate); + if (saveProcessGroup) + m_processGroup = tcgetpgrp (0); + else + m_processGroup = -1; + } + else + { + m_fd = -1; + m_tflags = -1; + m_ttystateErr = -1; + m_processGroup = -1; + } + return m_ttystateErr == 0; +} + +bool +TTYState::SetTTYState () const +{ + int result = 0; + if (IsValid()) + { + if (TFlagsValid()) + result = fcntl (m_fd, F_SETFL, m_tflags); + + if (TTYStateValid()) + result = tcsetattr (m_fd, TCSANOW, &m_ttystate); + + if (ProcessGroupValid()) + { + // Save the original signal handler. + void (*saved_sigttou_callback) (int) = NULL; + saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN); + // Set the process group + result = tcsetpgrp (m_fd, m_processGroup); + // Restore the original signal handler. + signal (SIGTTOU, saved_sigttou_callback); + } + return true; + } + return false; +} + + + +TTYStateSwitcher::TTYStateSwitcher() : + m_currentState(~0) +{ +} + +TTYStateSwitcher::~TTYStateSwitcher() +{ +} + +bool +TTYStateSwitcher::GetState(uint32_t idx, int fd, bool saveProcessGroup) +{ + if (ValidStateIndex(idx)) + return m_ttystates[idx].GetTTYState(fd, saveProcessGroup); + return false; +} + +bool +TTYStateSwitcher::SetState(uint32_t idx) const +{ + if (!ValidStateIndex(idx)) + return false; + + // See if we already are in this state? + if (ValidStateIndex(m_currentState) && (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].SetTTYState()) + { + m_currentState = idx; + return true; + } + + // We failed to set the state. The tty state was invalid or not + // initialized. + return false; +} + diff --git a/lldb/tools/debugserver/source/TTYState.h b/lldb/tools/debugserver/source/TTYState.h new file mode 100644 index 000000000000..c01d51255439 --- /dev/null +++ b/lldb/tools/debugserver/source/TTYState.h @@ -0,0 +1,61 @@ +//===-- TTYState.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Created by Greg Clayton on 3/26/07. +// +//===----------------------------------------------------------------------===// + +#ifndef __TTYState_h__ +#define __TTYState_h__ + +#include +#include + +class TTYState +{ +public: + TTYState(); + ~TTYState(); + + bool GetTTYState (int fd, bool saveProcessGroup); + bool SetTTYState () const; + + bool IsValid() const { return FileDescriptorValid() && TFlagsValid() && TTYStateValid(); } + bool FileDescriptorValid() const { return m_fd >= 0; } + bool TFlagsValid() const { return m_tflags != -1; } + bool TTYStateValid() const { return m_ttystateErr == 0; } + bool ProcessGroupValid() const { return m_processGroup != -1; } + +protected: + int m_fd; // File descriptor + int m_tflags; + int m_ttystateErr; + struct termios m_ttystate; + pid_t m_processGroup; + +}; + + +class TTYStateSwitcher +{ +public: + TTYStateSwitcher(); + ~TTYStateSwitcher(); + + bool GetState(uint32_t idx, int fd, bool saveProcessGroup); + bool SetState(uint32_t idx) const; + uint32_t NumStates() const { return sizeof(m_ttystates)/sizeof(TTYState); } + bool ValidStateIndex(uint32_t idx) const { return idx < NumStates(); } + +protected: + mutable uint32_t m_currentState; + TTYState m_ttystates[2]; +}; + +#endif \ No newline at end of file diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist b/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist new file mode 100644 index 000000000000..4e847c20a718 --- /dev/null +++ b/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist @@ -0,0 +1,16 @@ + + + + + Label + com.apple.debugserver.applist + UserName + mobile + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + --applist + + + diff --git a/lldb/tools/debugserver/source/com.apple.debugserver.plist b/lldb/tools/debugserver/source/com.apple.debugserver.plist new file mode 100644 index 000000000000..aa72606e0982 --- /dev/null +++ b/lldb/tools/debugserver/source/com.apple.debugserver.plist @@ -0,0 +1,15 @@ + + + + + Label + com.apple.debugserver + UserName + mobile + ProgramArguments + + /Developer/usr/bin/debugserver + --lockdown + + + diff --git a/lldb/tools/debugserver/source/debugserver-entitlements.plist b/lldb/tools/debugserver/source/debugserver-entitlements.plist new file mode 100644 index 000000000000..ce8bd5bc4c09 --- /dev/null +++ b/lldb/tools/debugserver/source/debugserver-entitlements.plist @@ -0,0 +1,14 @@ + + + + + com.apple.springboard.debugapplications + + run-unsigned-code + + seatbelt-profiles + + debugserver + + + diff --git a/lldb/tools/debugserver/source/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp new file mode 100644 index 000000000000..f3041dbbf3ac --- /dev/null +++ b/lldb/tools/debugserver/source/debugserver.cpp @@ -0,0 +1,1219 @@ +//===-- debugserver.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 +#include +#include + +#include "CFString.h" +#include "DNB.h" +#include "DNBLog.h" +#include "DNBTimer.h" +#include "PseudoTerminal.h" +#include "RNBContext.h" +#include "RNBServices.h" +#include "RNBSocket.h" +#include "RNBRemote.h" +#include "SysSignal.h" + +// Global PID in case we get a signal and need to stop the process... +nub_process_t g_pid = INVALID_NUB_PROCESS; + +//---------------------------------------------------------------------- +// Run loop modes which determine which run loop function will be called +//---------------------------------------------------------------------- +typedef enum +{ + eRNBRunLoopModeInvalid = 0, + eRNBRunLoopModeGetStartModeFromRemoteProtocol, + eRNBRunLoopModeInferiorAttaching, + eRNBRunLoopModeInferiorLaunching, + eRNBRunLoopModeInferiorExecuting, + eRNBRunLoopModeExit +} RNBRunLoopMode; + + +//---------------------------------------------------------------------- +// Global Variables +//---------------------------------------------------------------------- +RNBRemoteSP g_remoteSP; +static int g_lockdown_opt = 0; +static int g_applist_opt = 0; +static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault; + +int g_isatty = 0; + +#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) +#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) + +//---------------------------------------------------------------------- +// Run Loop function prototypes +//---------------------------------------------------------------------- +RNBRunLoopMode RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remote); +RNBRunLoopMode RNBRunLoopInferiorExecuting (RNBRemoteSP &remote); + + +//---------------------------------------------------------------------- +// Get our program path and arguments from the remote connection. +// We will need to start up the remote connection without a PID, get the +// arguments, wait for the new process to finish launching and hit its +// entry point, and then return the run loop mode that should come next. +//---------------------------------------------------------------------- +RNBRunLoopMode +RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remoteSP) +{ + std::string packet; + + if (remoteSP.get() != NULL) + { + RNBRemote* remote = remoteSP.get(); + RNBContext& ctx = remote->Context(); + uint32_t event_mask = RNBContext::event_read_packet_available; + + // Spin waiting to get the A packet. + while (1) + { + DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask); + nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); + DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events); + + if (set_events & RNBContext::event_read_packet_available) + { + rnb_err_t err = rnb_err; + RNBRemote::PacketEnum type; + + err = remote->HandleReceivedPacket (&type); + + // check if we tried to attach to a process + if (type == RNBRemote::vattach || type == RNBRemote::vattachwait) + { + if (err == rnb_success) + return eRNBRunLoopModeInferiorExecuting; + else + { + RNBLogSTDERR ("error: attach failed."); + return eRNBRunLoopModeExit; + } + } + + if (err == rnb_success) + { + // If we got our arguments we are ready to launch using the arguments + // and any environment variables we received. + if (type == RNBRemote::set_argv) + { + return eRNBRunLoopModeInferiorLaunching; + } + } + else if (err == rnb_not_connected) + { + RNBLogSTDERR ("error: connection lost."); + return eRNBRunLoopModeExit; + } + else + { + // a catch all for any other gdb remote packets that failed + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__); + continue; + } + + DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__); + } + else + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__); + return eRNBRunLoopModeExit; + } + } + } + return eRNBRunLoopModeExit; +} + + +//---------------------------------------------------------------------- +// This run loop mode will wait for the process to launch and hit its +// entry point. It will currently ignore all events except for the +// process state changed event, where it watches for the process stopped +// or crash process state. +//---------------------------------------------------------------------- +RNBRunLoopMode +RNBRunLoopLaunchInferior (RNBRemoteSP &remote, const char *stdio_path) +{ + RNBContext& ctx = remote->Context(); + + // The Process stuff takes a c array, the RNBContext has a vector... + // So make up a c array. + + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__, ctx.ArgumentAtIndex(0)); + + size_t inferior_argc = ctx.ArgumentCount(); + // Initialize inferior_argv with inferior_argc + 1 NULLs + std::vector inferior_argv(inferior_argc + 1, NULL); + + size_t i; + for (i = 0; i < inferior_argc; i++) + inferior_argv[i] = ctx.ArgumentAtIndex(i); + + // Pass the environment array the same way: + + size_t inferior_envc = ctx.EnvironmentCount(); + // Initialize inferior_argv with inferior_argc + 1 NULLs + std::vector inferior_envp(inferior_envc + 1, NULL); + + for (i = 0; i < inferior_envc; i++) + inferior_envp[i] = ctx.EnvironmentAtIndex(i); + + // Our launch type hasn't been set to anything concrete, so we need to + // figure our how we are going to launch automatically. + + nub_launch_flavor_t launch_flavor = g_launch_flavor; + if (launch_flavor == eLaunchFlavorDefault) + { + // Our default launch method is posix spawn + launch_flavor = eLaunchFlavorPosixSpawn; + +#if defined (__arm__) + // Check if we have an app bundle, if so launch using SpringBoard. + if (strstr(inferior_argv[0], ".app")) + { + launch_flavor = eLaunchFlavorSpringBoard; + } +#endif + } + + ctx.SetLaunchFlavor(launch_flavor); + char resolved_path[PATH_MAX]; + + // If we fail to resolve the path to our executable, then just use what we + // were given and hope for the best + if ( !DNBResolveExecutablePath (inferior_argv[0], resolved_path, sizeof(resolved_path)) ) + ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path)); + + char launch_err_str[PATH_MAX]; + launch_err_str[0] = '\0'; + nub_process_t pid = DNBProcessLaunch (resolved_path, + &inferior_argv[0], + &inferior_envp[0], + stdio_path, + launch_flavor, + launch_err_str, + sizeof(launch_err_str)); + + g_pid = pid; + + if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0) + { + DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__); + ctx.LaunchStatus().SetError(-1, DNBError::Generic); + ctx.LaunchStatus().SetErrorString(launch_err_str); + } + else + ctx.LaunchStatus().Clear(); + + if (remote->Comm().IsConnected()) + { + // It we are connected already, the next thing gdb will do is ask + // whether the launch succeeded, and if not, whether there is an + // error code. So we need to fetch one packet from gdb before we wait + // on the stop from the target. + + uint32_t event_mask = RNBContext::event_read_packet_available; + nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); + + if (set_events & RNBContext::event_read_packet_available) + { + rnb_err_t err = rnb_err; + RNBRemote::PacketEnum type; + + err = remote->HandleReceivedPacket (&type); + + if (err != rnb_success) + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__); + return eRNBRunLoopModeExit; + } + if (type != RNBRemote::query_launch_success) + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__); + } + } + } + + while (pid != INVALID_NUB_PROCESS) + { + // Wait for process to start up and hit entry point + DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid); + nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL); + DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events); + + if (set_events == 0) + { + pid = INVALID_NUB_PROCESS; + g_pid = pid; + } + else + { + if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged)) + { + nub_state_t pid_state = DNBProcessGetState (pid); + DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state)); + + switch (pid_state) + { + default: + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateSuspended: + break; // Ignore + + case eStateRunning: + case eStateStepping: + // Still waiting to stop at entry point... + break; + + case eStateStopped: + case eStateCrashed: + ctx.SetProcessID(pid); + return eRNBRunLoopModeInferiorExecuting; + + case eStateDetached: + case eStateExited: + pid = INVALID_NUB_PROCESS; + g_pid = pid; + return eRNBRunLoopModeExit; + } + } + + DNBProcessResetEvents(pid, set_events); + } + } + + return eRNBRunLoopModeExit; +} + + +//---------------------------------------------------------------------- +// This run loop mode will wait for the process to launch and hit its +// entry point. It will currently ignore all events except for the +// process state changed event, where it watches for the process stopped +// or crash process state. +//---------------------------------------------------------------------- +RNBRunLoopMode +RNBRunLoopLaunchAttaching (RNBRemoteSP &remote, nub_process_t attach_pid, nub_process_t& pid) +{ + RNBContext& ctx = remote->Context(); + + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid); + char err_str[1024]; + pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str)); + g_pid = pid; + + if (pid == INVALID_NUB_PROCESS) + { + ctx.LaunchStatus().SetError(-1, DNBError::Generic); + if (err_str[0]) + ctx.LaunchStatus().SetErrorString(err_str); + return eRNBRunLoopModeExit; + } + else + { + + ctx.SetProcessID(pid); + return eRNBRunLoopModeInferiorExecuting; + } +} + +//---------------------------------------------------------------------- +// Watch for signals: +// SIGINT: so we can halt our inferior. (disabled for now) +// SIGPIPE: in case our child process dies +//---------------------------------------------------------------------- +int g_sigint_received = 0; +int g_sigpipe_received = 0; +void +signal_handler(int signo) +{ + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo)); + + switch (signo) + { + case SIGINT: + g_sigint_received++; + if (g_pid != INVALID_NUB_PROCESS) + { + // Only send a SIGINT once... + if (g_sigint_received == 1) + { + switch (DNBProcessGetState (g_pid)) + { + case eStateRunning: + case eStateStepping: + DNBProcessSignal (g_pid, SIGSTOP); + return; + } + } + } + exit (SIGINT); + break; + + case SIGPIPE: + g_sigpipe_received = 1; + break; + } +} + +// Return the new run loop mode based off of the current process state +RNBRunLoopMode +HandleProcessStateChange (RNBRemoteSP &remote, bool initialize) +{ + RNBContext& ctx = remote->Context(); + nub_process_t pid = ctx.ProcessID(); + + if (pid == INVALID_NUB_PROCESS) + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__); + return eRNBRunLoopModeExit; + } + nub_state_t pid_state = DNBProcessGetState (pid); + + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state)); + + switch (pid_state) + { + case eStateInvalid: + case eStateUnloaded: + // Something bad happened + return eRNBRunLoopModeExit; + break; + + case eStateAttaching: + case eStateLaunching: + return eRNBRunLoopModeInferiorExecuting; + + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + // If we stop due to a signal, so clear the fact that we got a SIGINT + // so we can stop ourselves again (but only while our inferior + // process is running..) + g_sigint_received = 0; + if (initialize == false) + { + // Compare the last stop count to our current notion of a stop count + // to make sure we don't notify more than once for a given stop. + nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount(); + bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid)); + if (pid_stop_count_changed) + { + remote->FlushSTDIO(); + + if (ctx.GetProcessStopCount() == 1) + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); + } + else + { + + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); + remote->NotifyThatProcessStopped (); + } + } + else + { + DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); + } + } + return eRNBRunLoopModeInferiorExecuting; + + case eStateStepping: + case eStateRunning: + return eRNBRunLoopModeInferiorExecuting; + + case eStateExited: + remote->HandlePacket_last_signal(NULL); + return eRNBRunLoopModeExit; + + } + + // Catch all... + return eRNBRunLoopModeExit; +} +// This function handles the case where our inferior program is stopped and +// we are waiting for gdb remote protocol packets. When a packet occurs that +// makes the inferior run, we need to leave this function with a new state +// as the return code. +RNBRunLoopMode +RNBRunLoopInferiorExecuting (RNBRemoteSP &remote) +{ + DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__); + RNBContext& ctx = remote->Context(); + + // Init our mode and set 'is_running' based on the current process state + RNBRunLoopMode mode = HandleProcessStateChange (remote, true); + + while (ctx.ProcessID() != INVALID_NUB_PROCESS) + { + + std::string set_events_str; + uint32_t event_mask = ctx.NormalEventBits(); + + if (!ctx.ProcessStateRunning()) + { + // Clear the stdio bits if we are not running so we don't send any async packets + event_mask &= ~RNBContext::event_proc_stdio_available; + } + + // We want to make sure we consume all process state changes and have + // whomever is notifying us to wait for us to reset the event bit before + // continuing. + //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed); + + DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask); + nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); + DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str)); + + if (set_events) + { + if ((set_events & RNBContext::event_proc_thread_exiting) || + (set_events & RNBContext::event_proc_stdio_available)) + { + remote->FlushSTDIO(); + } + + if (set_events & RNBContext::event_read_packet_available) + { + // handleReceivedPacket will take care of resetting the + // event_read_packet_available events when there are no more... + set_events ^= RNBContext::event_read_packet_available; + + if (ctx.ProcessStateRunning()) + { + if (remote->HandleAsyncPacket() == rnb_not_connected) + { + // TODO: connect again? Exit? + } + } + else + { + if (remote->HandleReceivedPacket() == rnb_not_connected) + { + // TODO: connect again? Exit? + } + } + } + + if (set_events & RNBContext::event_proc_state_changed) + { + mode = HandleProcessStateChange (remote, false); + ctx.Events().ResetEvents(RNBContext::event_proc_state_changed); + set_events ^= RNBContext::event_proc_state_changed; + } + + if (set_events & RNBContext::event_proc_thread_exiting) + { + mode = eRNBRunLoopModeExit; + } + + if (set_events & RNBContext::event_read_thread_exiting) + { + // Out remote packet receiving thread exited, exit for now. + if (ctx.HasValidProcessID()) + { + // TODO: We should add code that will leave the current process + // in its current state and listen for another connection... + if (ctx.ProcessStateRunning()) + { + DNBProcessKill (ctx.ProcessID()); + } + } + mode = eRNBRunLoopModeExit; + } + } + + // Reset all event bits that weren't reset for now... + if (set_events != 0) + ctx.Events().ResetEvents(set_events); + + if (mode != eRNBRunLoopModeInferiorExecuting) + break; + } + + return mode; +} + + +//---------------------------------------------------------------------- +// Convenience function to set up the remote listening port +// Returns 1 for success 0 for failure. +//---------------------------------------------------------------------- + +static int +StartListening (RNBRemoteSP remoteSP, int listen_port) +{ + if (!remoteSP->Comm().IsConnected()) + { + RNBLogSTDOUT ("Listening to port %i...\n", listen_port); + if (remoteSP->Comm().Listen(listen_port) != rnb_success) + { + RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); + return 0; + } + else + { + remoteSP->StartReadRemoteDataThread(); + } + } + return 1; +} + +//---------------------------------------------------------------------- +// ASL Logging callback that can be registered with DNBLogSetLogCallback +//---------------------------------------------------------------------- +void +ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args) +{ + if (format == NULL) + return; + static aslmsg g_aslmsg = NULL; + if (g_aslmsg == NULL) + { + g_aslmsg = ::asl_new (ASL_TYPE_MSG); + char asl_key_sender[PATH_MAX]; + snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%g", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_NUM); + ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); + } + + int asl_level; + if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT; + else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR; + else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING; + else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO; + else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG; + + ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); +} + +//---------------------------------------------------------------------- +// FILE based Logging callback that can be registered with +// DNBLogSetLogCallback +//---------------------------------------------------------------------- +void +FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args) +{ + if (baton == NULL || format == NULL) + return; + + ::vfprintf ((FILE *)baton, format, args); + ::fprintf ((FILE *)baton, "\n"); +} + + +void +show_usage_and_exit (int exit_code) +{ + RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME); + RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME); + RNBLogSTDERR (" %s host:port --attach=\n", DEBUGSERVER_PROGRAM_NAME); + RNBLogSTDERR (" %s /path/file --attach=\n", DEBUGSERVER_PROGRAM_NAME); + RNBLogSTDERR (" %s host:port --attach=\n", DEBUGSERVER_PROGRAM_NAME); + RNBLogSTDERR (" %s /path/file --attach=\n", DEBUGSERVER_PROGRAM_NAME); + exit (exit_code); +} + + +//---------------------------------------------------------------------- +// option descriptors for getopt_long() +//---------------------------------------------------------------------- +static struct option g_long_options[] = +{ + { "attach", required_argument, NULL, 'a' }, + { "debug", no_argument, NULL, 'g' }, + { "verbose", no_argument, NULL, 'v' }, + { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k" + { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t" + { "log-file", required_argument, NULL, 'l' }, + { "log-flags", required_argument, NULL, 'f' }, + { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only) + { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG + { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name + { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name + { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture. + { "stdio-path", required_argument, NULL, 's' }, // Set the STDIO path to be used when launching applications + { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own sessions + { NULL, 0, NULL, 0 } +}; + + +//---------------------------------------------------------------------- +// main +//---------------------------------------------------------------------- +int +main (int argc, char *argv[]) +{ + g_isatty = ::isatty (STDIN_FILENO); + + // ::printf ("uid=%u euid=%u gid=%u egid=%u\n", + // getuid(), + // geteuid(), + // getgid(), + // getegid()); + + + // signal (SIGINT, signal_handler); + signal (SIGPIPE, signal_handler); + + int i; + int attach_pid = INVALID_NUB_PROCESS; + + FILE* log_file = NULL; + uint32_t log_flags = 0; + // Parse our options + int ch; + int long_option_index = 0; + int use_native_registers = 0; + int debug = 0; + std::string compile_options; + std::string waitfor_pid_name; // Wait for a process that starts with this name + std::string attach_pid_name; + std::string stdio_path; + useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec. + useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever. + +#if !defined (DNBLOG_ENABLED) + compile_options += "(no-logging) "; +#endif + + RNBRunLoopMode start_mode = eRNBRunLoopModeExit; + + while ((ch = getopt_long(argc, argv, "a:d:gi:vktl:f:w:x:r", g_long_options, &long_option_index)) != -1) + { + DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n", + ch, (uint8_t)ch, + g_long_options[long_option_index].name, + g_long_options[long_option_index].has_arg ? '=' : ' ', + optarg ? optarg : ""); + switch (ch) + { + case 0: // Any optional that auto set themselves will return 0 + break; + + case 'a': + if (optarg && optarg[0]) + { + if (isdigit(optarg[0])) + { + char *end = NULL; + attach_pid = strtoul(optarg, &end, 0); + if (end == NULL || *end != '\0') + { + RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg); + exit (4); + } + } + else + { + attach_pid_name = optarg; + } + start_mode = eRNBRunLoopModeInferiorAttaching; + } + break; + + // --waitfor=NAME + case 'w': + if (optarg && optarg[0]) + { + waitfor_pid_name = optarg; + start_mode = eRNBRunLoopModeInferiorAttaching; + } + break; + + // --waitfor-interval=USEC + case 'i': + if (optarg && optarg[0]) + { + char *end = NULL; + waitfor_interval = strtoul(optarg, &end, 0); + if (end == NULL || *end != '\0') + { + RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg); + exit (6); + } + } + break; + + // --waitfor-duration=SEC + case 'd': + if (optarg && optarg[0]) + { + char *end = NULL; + waitfor_duration = strtoul(optarg, &end, 0); + if (end == NULL || *end != '\0') + { + RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg); + exit (7); + } + } + break; + + case 'x': + if (optarg && optarg[0]) + { + if (strcasecmp(optarg, "auto") == 0) + g_launch_flavor = eLaunchFlavorDefault; + else if (strcasestr(optarg, "posix") == optarg) + g_launch_flavor = eLaunchFlavorPosixSpawn; + else if (strcasestr(optarg, "fork") == optarg) + g_launch_flavor = eLaunchFlavorForkExec; +#if defined (__arm__) + else if (strcasestr(optarg, "spring") == optarg) + g_launch_flavor = eLaunchFlavorSpringBoard; +#endif + else + { + RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg); + RNBLogSTDERR ("Valid values TYPE are:\n"); + RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n"); + RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n"); + RNBLogSTDERR (" fork Launch the executable using fork and exec.\n"); +#if defined (__arm__) + RNBLogSTDERR (" spring Launch the executable through Springboard.\n"); +#endif + exit (5); + } + } + break; + + case 'l': // Set Log File + if (optarg && optarg[0]) + { + if (strcasecmp(optarg, "stdout") == 0) + log_file = stdout; + else if (strcasecmp(optarg, "stderr") == 0) + log_file = stderr; + else + log_file = fopen(optarg, "w+"); + + if (log_file == NULL) + { + const char *errno_str = strerror(errno); + RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error"); + } + } + break; + + case 'f': // Log Flags + if (optarg && optarg[0]) + log_flags = strtoul(optarg, NULL, 0); + break; + + case 'g': + debug = 1; + DNBLogSetDebug(1); + break; + + case 't': + g_applist_opt = 1; + break; + + case 'k': + g_lockdown_opt = 1; + break; + + case 'r': + use_native_registers = 1; + break; + + case 'v': + DNBLogSetVerbose(1); + break; + + case 's': + stdio_path = optarg; + break; + + case 'S': + // Put debugserver into a new session. Terminals group processes + // into sessions and when a special terminal key sequences + // (like control+c) are typed they can cause signals to go out to + // all processes in a session. Using this --setsid (-S) option + // will cause debugserver to run in its own sessions and be free + // from such issues. + // + // This is useful when debugserver is spawned from a command + // line application that uses debugserver to do the debugging, + // yet that application doesn't want debugserver receiving the + // signals sent to the session (i.e. dying when anyone hits ^C). + setsid(); + break; + } + } + + // Skip any options we consumed with getopt_long + argc -= optind; + argv += optind; + + g_remoteSP.reset (new RNBRemote (use_native_registers)); + + RNBRemote *remote = g_remoteSP.get(); + if (remote == NULL) + { + RNBLogSTDERR ("error: failed to create a remote connection class\n"); + return -1; + } + + RNBContext& ctx = remote->Context(); + + + // It is ok for us to set NULL as the logfile (this will disable any logging) + + if (log_file != NULL) + { + DNBLogSetLogCallback(FileLogCallback, log_file); + // If our log file was set, yet we have no log flags, log everything! + if (log_flags == 0) + log_flags = LOG_ALL | LOG_RNB_ALL; + + DNBLogSetLogMask (log_flags); + } + else + { + // Enable DNB logging + DNBLogSetLogCallback(ASLLogCallback, NULL); + DNBLogSetLogMask (log_flags); + + } + + if (DNBLogEnabled()) + { + for (i=0; iComm().IsConnected()) + { + if (g_remoteSP->Comm().ConnectToService () != rnb_success) + { + RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); + mode = eRNBRunLoopModeExit; + } + else if (g_applist_opt != 0) + { + // List all applications we are able to see + std::string applist_plist; + if (ListApplications(applist_plist, false, false) == 0) + { + DNBLogDebug("Task list: %s", applist_plist.c_str()); + + g_remoteSP->Comm().Write(applist_plist.c_str(), applist_plist.size()); + // Issue a read that will never yield any data until the other side + // closes the socket so this process doesn't just exit and cause the + // socket to close prematurely on the other end and cause data loss. + std::string buf; + g_remoteSP->Comm().Read(buf); + } + g_remoteSP->Comm().Disconnect(false); + mode = eRNBRunLoopModeExit; + break; + } + else + { + // Start watching for remote packets + g_remoteSP->StartReadRemoteDataThread(); + } + } + } + else +#endif + if (listen_port != INT32_MAX) + { + if (!StartListening (g_remoteSP, listen_port)) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (g_remoteSP->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + if (mode != eRNBRunLoopModeExit) + { + RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); + + mode = RNBRunLoopGetStartModeFromRemote (g_remoteSP); + } + break; + + case eRNBRunLoopModeInferiorAttaching: + if (!waitfor_pid_name.empty()) + { + // Set our end wait time if we are using a waitfor-duration + // option that may have been specified + struct timespec attach_timeout_abstime, *timeout_ptr = NULL; + if (waitfor_duration != 0) + { + DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0); + timeout_ptr = &attach_timeout_abstime; + } + nub_launch_flavor_t launch_flavor = g_launch_flavor; + if (launch_flavor == eLaunchFlavorDefault) + { + // Our default launch method is posix spawn + launch_flavor = eLaunchFlavorPosixSpawn; + +#if defined (__arm__) + // Check if we have an app bundle, if so launch using SpringBoard. + if (waitfor_pid_name.find (".app") != std::string::npos) + { + launch_flavor = eLaunchFlavorSpringBoard; + } +#endif + } + + ctx.SetLaunchFlavor(launch_flavor); + + nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str)); + g_pid = pid; + + if (pid == INVALID_NUB_PROCESS) + { + ctx.LaunchStatus().SetError(-1, DNBError::Generic); + if (err_str[0]) + ctx.LaunchStatus().SetErrorString(err_str); + RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str); + mode = eRNBRunLoopModeExit; + } + else + { + ctx.SetProcessID(pid); + mode = eRNBRunLoopModeInferiorExecuting; + } + } + else if (attach_pid != INVALID_NUB_PROCESS) + { + + RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid); + nub_process_t attached_pid; + mode = RNBRunLoopLaunchAttaching (g_remoteSP, attach_pid, attached_pid); + if (mode != eRNBRunLoopModeInferiorExecuting) + { + const char *error_str = remote->Context().LaunchStatus().AsString(); + RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error."); + mode = eRNBRunLoopModeExit; + } + } + else if (!attach_pid_name.empty ()) + { + struct timespec attach_timeout_abstime, *timeout_ptr = NULL; + if (waitfor_duration != 0) + { + DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0); + timeout_ptr = &attach_timeout_abstime; + } + + nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str)); + g_pid = pid; + if (pid == INVALID_NUB_PROCESS) + { + ctx.LaunchStatus().SetError(-1, DNBError::Generic); + if (err_str[0]) + ctx.LaunchStatus().SetErrorString(err_str); + RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str); + mode = eRNBRunLoopModeExit; + } + else + { + ctx.SetProcessID(pid); + mode = eRNBRunLoopModeInferiorExecuting; + } + + } + else + { + RNBLogSTDERR ("error: asked to attach with empty name and invalid PID."); + mode = eRNBRunLoopModeExit; + } + + if (mode != eRNBRunLoopModeExit) + { + if (listen_port != INT32_MAX) + { + if (!StartListening (g_remoteSP, listen_port)) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (g_remoteSP->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + if (mode != eRNBRunLoopModeExit) + RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid); + } + break; + + case eRNBRunLoopModeInferiorLaunching: + mode = RNBRunLoopLaunchInferior (g_remoteSP, stdio_path.empty() ? NULL : stdio_path.c_str()); + + if (mode == eRNBRunLoopModeInferiorExecuting) + { + if (listen_port != INT32_MAX) + { + if (!StartListening (g_remoteSP, listen_port)) + mode = eRNBRunLoopModeExit; + } + else if (str[0] == '/') + { + if (g_remoteSP->Comm().OpenFile (str)) + mode = eRNBRunLoopModeExit; + } + + if (mode != eRNBRunLoopModeExit) + RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n"); + } + else + { + const char *error_str = remote->Context().LaunchStatus().AsString(); + RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error."); + } + break; + + case eRNBRunLoopModeInferiorExecuting: + mode = RNBRunLoopInferiorExecuting(g_remoteSP); + break; + + default: + mode = eRNBRunLoopModeExit; + case eRNBRunLoopModeExit: + break; + } + } + + g_remoteSP->StopReadRemoteDataThread (); + g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS); + + return 0; +} diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp new file mode 100644 index 000000000000..8cca697dd278 --- /dev/null +++ b/lldb/tools/driver/Driver.cpp @@ -0,0 +1,1265 @@ +//===-- Driver.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Driver.h" + +#include +#include +#include +#include +#include + +#include + +#include "IOChannel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace lldb; + +static void reset_stdin_termios (); +static struct termios g_old_stdin_termios; + +// In the Driver::MainLoop, we change the terminal settings. This function is +// added as an atexit handler to make sure we clean them up. +static void +reset_stdin_termios () +{ + ::tcsetattr (STDIN_FILENO, TCSANOW, &g_old_stdin_termios); +} + +static lldb::OptionDefinition g_options[] = +{ + { 0, true, "help", 'h', no_argument, NULL, NULL, NULL, + "Prints out the usage information for the LLDB debugger." }, + + { 1, true, "version", 'v', no_argument, NULL, NULL, NULL, + "Prints out the current version number of the LLDB debugger." }, + + { 2, false, "file", 'f', required_argument, NULL, NULL, "", + "Tells the debugger to use the file as the program to be debugged." }, + + { 2, false, "arch", 'a', required_argument, NULL, NULL, "", + "Tells the debugger to use the specified architecture when starting and running the program. must be one of the architectures for which the program was compiled." }, + + { 2, false, "script-language",'l', required_argument, NULL, NULL, "", + "Tells the debugger to use the specified scripting language for user-defined scripts, rather than the default. Valid scripting languages that can be specified include Python, Perl, Ruby and Tcl. Currently only the Python extensions have been implemented." }, + + { 2, false, "debug", 'd', no_argument, NULL, NULL, NULL, + "Tells the debugger to print out extra information for debugging itself." }, + + { 2, false, "source", 's', required_argument, NULL, NULL, "", + "Tells the debugger to read in and execute the file , which should contain lldb commands." }, + + { 3, false, "crash-log", 'c', required_argument, NULL, NULL, "", + "Load executable images from a crash log for symbolication." }, + + { 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + + +Driver::Driver () : + SBBroadcaster ("Driver"), + m_editline_pty (), + m_editline_slave_fh (NULL), + m_editline_reader (), + m_io_channel_ap (), + m_option_data (), + m_waiting_for_command (false) +{ +} + +Driver::~Driver () +{ +} + +void +Driver::CloseIOChannelFile () +{ + // Write and End of File sequence to the file descriptor to ensure any + // read functions can exit. + char eof_str[] = "\x04"; + ::write (m_editline_pty.GetMasterFileDescriptor(), eof_str, strlen(eof_str)); + + m_editline_pty.CloseMasterFileDescriptor(); + + if (m_editline_slave_fh) + { + ::fclose (m_editline_slave_fh); + m_editline_slave_fh = NULL; + } +} + +// This function takes INDENT, which tells how many spaces to output at the front of each line; SPACES, which is +// a string that is output_max_columns long, containing 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 +OutputFormattedUsageText (FILE *out, int indent, char *spaces, const char *text, int output_max_columns) +{ + int len = strlen (text); + std::string text_string (text); + std::string spaces_string (spaces); + + // Force indentation to be reasonable. + if (indent >= output_max_columns) + indent = 0; + + // Will it all fit on one line? + + if (len + indent < output_max_columns) + // Output as a single line + fprintf (out, "%s%s\n", spaces_string.substr (0, indent).c_str(), text); + else + { + // We need to break it up into multiple lines. + int text_width = output_max_columns - indent - 1; + int start = 0; + int end = start; + int final_end = len; + int sub_len; + + while (end < final_end) + { + // Dont 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; + std::string substring = text_string.substr (start, sub_len); + fprintf (out, "%s%s\n", spaces_string.substr(0, indent).c_str(), substring.c_str()); + start = end + 1; + } + } +} + +void +ShowUsage (FILE *out, lldb::OptionDefinition *option_table, Driver::OptionData data) +{ + uint32_t screen_width = 80; + uint32_t indent_level = 0; + const char *name = "lldb"; + char spaces[screen_width+1]; + uint32_t i; + + for (i = 0; i < screen_width; ++i) + spaces[i] = ' '; + spaces[i] = '\n'; + + std::string spaces_string (spaces); + + fprintf (out, "\nUsage:\n\n"); + + indent_level += 2; + + + // First, show each usage level set of options, e.g. [options-for-level-0] + // [options-for-level-1] + // etc. + + uint32_t usage_level = 0; + uint32_t num_options; + + for (num_options = 0; option_table[num_options].long_option != NULL; ++num_options); + + for (i = 0; i < num_options; ++i) + { + if (i == 0 || option_table[i].usage_level > usage_level) + { + // Start a new level. + usage_level = option_table[i].usage_level; + if (usage_level > 0) + fprintf (out, "\n\n"); + fprintf (out, "%s%s", spaces_string.substr(0, indent_level).c_str(), name); + } + + if (option_table[i].required) + { + if (option_table[i].option_has_arg == required_argument) + fprintf (out, " -%c %s", option_table[i].short_option, option_table[i].argument_name); + else if (option_table[i].option_has_arg == optional_argument) + fprintf (out, " -%c [%s]", option_table[i].short_option, option_table[i].argument_name); + else + fprintf (out, " -%c", option_table[i].short_option); + } + else + { + if (option_table[i].option_has_arg == required_argument) + fprintf (out, " [-%c %s]", option_table[i].short_option, option_table[i].argument_name); + else if (option_table[i].option_has_arg == optional_argument) + fprintf (out, " [-%c [%s]]", option_table[i].short_option, option_table[i].argument_name); + else + fprintf (out, " [-%c]", option_table[i].short_option); + } + } + + fprintf (out, "\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. + + Driver::OptionData::OptionSet options_seen; + Driver::OptionData::OptionSet::iterator pos; + + indent_level += 5; + + for (i = 0; i < num_options; ++i) + { + // Only print this option if we haven't already seen it. + pos = options_seen.find (option_table[i].short_option); + if (pos == options_seen.end()) + { + options_seen.insert (option_table[i].short_option); + fprintf (out, "%s-%c ", spaces_string.substr(0, indent_level).c_str(), option_table[i].short_option); + if (option_table[i].argument_name != NULL) + fprintf (out, "%s", option_table[i].argument_name); + fprintf (out, "\n"); + fprintf (out, "%s--%s ", spaces_string.substr(0, indent_level).c_str(), option_table[i].long_option); + if (option_table[i].argument_name != NULL) + fprintf (out, "%s", option_table[i].argument_name); + fprintf (out, "\n"); + indent_level += 5; + OutputFormattedUsageText (out, indent_level, spaces, option_table[i].usage_text, screen_width); + indent_level -= 5; + fprintf (out, "\n"); + } + } + + indent_level -= 5; + + fprintf (out, "\n%s('%s ' also works, to specify the file to be debugged.)\n\n", + spaces_string.substr(0, indent_level).c_str(), name); +} + +void +BuildGetOptTable (lldb::OptionDefinition *expanded_option_table, struct option **getopt_table, int num_options) +{ + if (num_options == 0) + return; + + uint32_t i; + uint32_t j; + std::bitset<256> option_seen; + + for (i = 0, j = 0; i < num_options; ++i) + { + char short_opt = expanded_option_table[i].short_option; + + if (option_seen.test(short_opt) == false) + { + (*getopt_table)[j].name = expanded_option_table[i].long_option; + (*getopt_table)[j].has_arg = expanded_option_table[i].option_has_arg; + (*getopt_table)[j].flag = NULL; + (*getopt_table)[j].val = expanded_option_table[i].short_option; + option_seen.set(short_opt); + ++j; + } + } + + (*getopt_table)[j].name = NULL; + (*getopt_table)[j].has_arg = 0; + (*getopt_table)[j].flag = NULL; + (*getopt_table)[j].val = 0; + +} + +SBError +ParseOptions (Driver::OptionData &data, int argc, const char **argv) +{ + SBError error; + std::string option_string; + struct option *long_options = NULL; + int num_options; + + for (num_options = 0; g_options[num_options].long_option != NULL; ++num_options); + + if (num_options == 0) + { + if (argc > 1) + error.SetErrorStringWithFormat ("invalid number of options"); + return error; + } + + long_options = (struct option *) malloc ((num_options + 1) * sizeof (struct option)); + + BuildGetOptTable (g_options, &long_options, num_options); + + if (long_options == NULL) + { + error.SetErrorStringWithFormat ("invalid long options"); + return error; + } + + // Build the option_string argument for call to getopt_long. + + for (int i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + option_string.push_back ((char) long_options[i].val); + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + option_string.push_back (':'); + break; + case optional_argument: + option_string.append ("::"); + break; + } + } + } + + // Prepare for & make calls to getopt_long. + + optreset = 1; + optind = 1; + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long (argc, (char * const *) argv, option_string.c_str(), long_options, &long_options_index); + + if (val == -1) + break; + else if (val == '?') + { + data.m_print_help = true; + error.SetErrorStringWithFormat ("unknown or ambiguous option"); + break; + } + else if (val == 0) + continue; + else + { + data.m_seen_options.insert ((char) val); + 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; + } + } + } + + if (long_options_index >= 0) + { + error = Driver::SetOptionValue (long_options_index, + long_options[long_options_index].has_arg == no_argument ? NULL : optarg, + data); + } + else + { + error.SetErrorStringWithFormat ("invalid option with value %i", val); + } + if (error.Fail()) + break; + } + } + + return error; +} + +Driver::OptionData::OptionData () : + m_filename(), + m_script_lang (lldb::eScriptLanguageDefault), + m_source_command_files (), + m_debug_mode (false), + m_print_help (false), + m_print_version (false) + +{ +} + +Driver::OptionData::~OptionData () +{ +} + +void +Driver::OptionData::Clear () +{ + m_filename.clear (); + m_script_lang = lldb::eScriptLanguageDefault; + m_source_command_files.clear (); + m_debug_mode = false; + m_print_help = false; + m_print_version = false; +} + +SBError +Driver::SetOptionValue (int option_idx, const char *option_arg, Driver::OptionData &option_data) +{ + SBError error; + const char short_option = (char) g_options[option_idx].short_option; + + switch (short_option) + { + case 'h': + option_data.m_print_help = true; + break; + + case 'v': + option_data.m_print_version = true; + break; + + case 'c': + option_data.m_crash_log = option_arg; + break; + + case 'f': + { + SBFileSpec file(option_arg); + if (file.Exists()) + option_data.m_filename = option_arg; + else + error.SetErrorStringWithFormat("file specified in --file (-f) option doesn't exist: '%s'", option_arg); + } + break; + + case 'a': + if (!SBDebugger::SetDefaultArchitecture (option_arg)) + error.SetErrorStringWithFormat("invalid architecture in the -a or --arch option: '%s'", option_arg); + break; + + case 'l': + option_data.m_script_lang = SBDebugger::GetScriptingLanguage (option_arg); + break; + + case 'd': + option_data.m_debug_mode = true; + break; + + case 's': + { + SBFileSpec file(option_arg); + if (file.Exists()) + option_data.m_source_command_files.push_back (option_arg); + else + error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", option_arg); + } + break; + + default: + option_data.m_print_help = true; + error.SetErrorStringWithFormat ("unrecognized option %c", short_option); + break; + } + + return error; +} + +void +Driver::ResetOptionValues () +{ + m_option_data.Clear (); +} + +const char * +Driver::GetFilename() const +{ + if (m_option_data.m_filename.empty()) + return NULL; + return m_option_data.m_filename.c_str(); +} + +const char * +Driver::GetCrashLogFilename() const +{ + if (m_option_data.m_crash_log.empty()) + return NULL; + return m_option_data.m_crash_log.c_str(); +} + +lldb::ScriptLanguage +Driver::GetScriptLanguage() const +{ + return m_option_data.m_script_lang; +} + +size_t +Driver::GetNumSourceCommandFiles () const +{ + return m_option_data.m_source_command_files.size(); +} + +const char * +Driver::GetSourceCommandFileAtIndex (uint32_t idx) const +{ + if (idx < m_option_data.m_source_command_files.size()) + return m_option_data.m_source_command_files[idx].c_str(); + return NULL; +} + +bool +Driver::GetDebugMode() const +{ + return m_option_data.m_debug_mode; +} + + +// Check the arguments that were passed to this program to make sure they are valid and to get their +// argument values (if any). Return a boolean value indicating whether or not to start up the full +// debugger (i.e. the Command Interpreter) or not. Return FALSE if the arguments were invalid OR +// if the user only wanted help or version information. + +bool +Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, FILE *err_fh) +{ + bool valid = true; + + ResetOptionValues (); + + if (argc == 2 && *(argv[1]) != '-') + { + m_option_data.m_filename = argv[1]; + } + else + { + SBCommandReturnObject result; + + SBError error = ParseOptions (m_option_data, argc, argv); + if (error.Fail()) + { + const char *error_cstr = error.GetCString (); + if (error_cstr) + ::fprintf (err_fh, "error: %s\n", error_cstr); + } + } + + // Check to see if they just invoked the debugger with a filename. + + + if (m_option_data.m_print_help) + { + ShowUsage (out_fh, g_options, m_option_data); + valid = false; + } + else if (m_option_data.m_print_version) + { + ::fprintf (out_fh, "%s\n", SBDebugger::GetVersionString()); + valid = false; + } + else if (! m_option_data.m_crash_log.empty()) + { + // Handle crash log stuff here. + } + else + { + // All other combinations are valid; do nothing more here. + } + + return valid; +} + +void +Driver::GetProcessSTDOUT () +{ + // The process has stuff waiting for stdout; get it and write it out to the appropriate place. + char stdio_buffer[1024]; + size_t len; + while ((len = SBDebugger::GetCurrentTarget().GetProcess().GetSTDOUT (stdio_buffer, sizeof (stdio_buffer))) > 0) + m_io_channel_ap->OutWrite (stdio_buffer, len); +} + +void +Driver::GetProcessSTDERR () +{ + // The process has stuff waiting for stderr; get it and write it out to the appropriate place. + char stdio_buffer[1024]; + size_t len; + while ((len = SBDebugger::GetCurrentTarget().GetProcess().GetSTDERR (stdio_buffer, sizeof (stdio_buffer))) > 0) + m_io_channel_ap->ErrWrite (stdio_buffer, len); +} + +void +Driver::UpdateCurrentThread () +{ + using namespace lldb; + SBProcess process(SBDebugger::GetCurrentTarget().GetProcess()); + if (process.IsValid()) + { + SBThread curr_thread (process.GetCurrentThread()); + SBThread thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + curr_thread_stop_reason = curr_thread.GetStopReason(); + + if (!curr_thread.IsValid() || + curr_thread_stop_reason == eStopReasonInvalid || + curr_thread_stop_reason == eStopReasonNone) + { + // Prefer a thread that has just completed its plan over another thread as current thread. + SBThread plan_thread; + SBThread other_thread; + const size_t num_threads = process.GetNumThreads(); + size_t i; + for (i = 0; i < num_threads; ++i) + { + thread = process.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread.GetStopReason(); + switch (thread_stop_reason) + { + default: + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + if (!other_thread.IsValid()) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread.IsValid()) + plan_thread = thread; + break; + } + } + if (plan_thread.IsValid()) + process.SetCurrentThread (plan_thread); + else if (other_thread.IsValid()) + process.SetCurrentThread (other_thread); + else + { + if (curr_thread.IsValid()) + thread = curr_thread; + else + thread = process.GetThreadAtIndex(0); + + if (thread.IsValid()) + process.SetCurrentThread (thread); + } + } + } +} + + +// This function handles events that were broadcast by the process. +void +Driver::HandleProcessEvent (const SBEvent &event) +{ + using namespace lldb; + const uint32_t event_type = event.GetType(); + + if (event_type & SBProcess::eBroadcastBitSTDOUT) + { + // The process has stdout available, get it and write it out to the + // appropriate place. + GetProcessSTDOUT (); + } + else if (event_type & SBProcess::eBroadcastBitSTDERR) + { + // The process has stderr available, get it and write it out to the + // appropriate place. + GetProcessSTDERR (); + } + else if (event_type & SBProcess::eBroadcastBitStateChanged) + { + // Drain all stout and stderr so we don't see any output come after + // we print our prompts + GetProcessSTDOUT (); + GetProcessSTDERR (); + + // Something changed in the process; get the event and report the process's current status and location to + // the user. + StateType event_state = SBProcess::GetStateFromEvent (event); + if (event_state == eStateInvalid) + return; + + SBProcess process (SBProcess::GetProcessFromEvent (event)); + assert (process.IsValid()); + + switch (event_state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + { + char message[1024]; + int message_len = ::snprintf (message, sizeof(message), "Process %d %s\n", process.GetProcessID(), + SBDebugger::StateAsCString (event_state)); + m_io_channel_ap->OutWrite(message, message_len); + } + break; + + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + SBDebugger::HandleCommand("status"); + m_io_channel_ap->RefreshPrompt(); + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (SBProcess::GetRestartedFromEvent (event)) + { + // FIXME: Do we want to report this, or would that just be annoyingly chatty? + char message[1024]; + int message_len = ::snprintf (message, sizeof(message), "Process %d stopped and was programmatically restarted.\n", + process.GetProcessID()); + m_io_channel_ap->OutWrite(message, message_len); + } + else + { + UpdateCurrentThread (); + SBDebugger::HandleCommand("status"); + m_io_channel_ap->RefreshPrompt(); + } + break; + } + } +} + +// This function handles events broadcast by the IOChannel (HasInput, UserInterrupt, or ThreadShouldExit). + +bool +Driver::HandleIOEvent (const SBEvent &event) +{ + bool quit = false; + + const uint32_t event_type = event.GetType(); + + if (event_type & IOChannel::eBroadcastBitHasUserInput) + { + // We got some input (i.e. a command string) from the user; pass it off to the command interpreter for + // handling. + + const char *command_string = SBEvent::GetCStringFromEvent(event); + if (command_string == NULL) + command_string == ""; + SBCommandReturnObject result; + if (SBDebugger::GetCommandInterpreter().HandleCommand (command_string, result, true) != lldb::eReturnStatusQuit) + { + m_io_channel_ap->ErrWrite (result.GetError(), result.GetErrorSize()); + m_io_channel_ap->OutWrite (result.GetOutput(), result.GetOutputSize()); + } + // We are done getting and running our command, we can now clear the + // m_waiting_for_command so we can get another one. + m_waiting_for_command = false; + + // If our editline input reader is active, it means another input reader + // got pushed onto the input reader and caused us to become deactivated. + // When the input reader above us gets popped, we will get re-activated + // and our prompt will refresh in our callback + if (m_editline_reader.IsActive()) + { + ReadyForCommand (); + } + } + else if (event_type & IOChannel::eBroadcastBitUserInterrupt) + { + // This is here to handle control-c interrupts from the user. It has not yet really been implemented. + // TO BE DONE: PROPERLY HANDLE CONTROL-C FROM USER + //m_io_channel_ap->CancelInput(); + // Anything else? Send Interrupt to process? + } + else if ((event_type & IOChannel::eBroadcastBitThreadShouldExit) || + (event_type & IOChannel::eBroadcastBitThreadDidExit)) + { + // If the IOChannel thread is trying to go away, then it is definitely + // time to end the debugging session. + quit = true; + } + + return quit; +} + + +//struct CrashImageInfo +//{ +// std::string path; +// VMRange text_range; +// UUID uuid; +//}; +// +//void +//Driver::ParseCrashLog (const char *crash_log) +//{ +// printf("Parsing crash log: %s\n", crash_log); +// +// char image_path[PATH_MAX]; +// std::vector crash_infos; +// if (crash_log && crash_log[0]) +// { +// FileSpec crash_log_file (crash_log); +// STLStringArray crash_log_lines; +// if (crash_log_file.ReadFileLines (crash_log_lines)) +// { +// const size_t num_crash_log_lines = crash_log_lines.size(); +// size_t i; +// for (i=0; i %s\n", +// text_start_addr, +// text_end_addr, +// uuid_cstr, +// image_path); +// } +// } +// } +// } +// } +// +// if (crash_infos.size()) +// { +// SBTarget target (SBDebugger::CreateTarget (crash_infos.front().path.c_str(), +// lldb::GetDefaultArchitecture().AsCString (), +// false)); +// if (target.IsValid()) +// { +// +// } +// } +// } +//} +// + +void +Driver::MasterThreadBytesReceived (void *baton, const void *src, size_t src_len) +{ + Driver *driver = (Driver*)baton; + driver->GetFromMaster ((const char *)src, src_len); +} + +void +Driver::GetFromMaster (const char *src, size_t src_len) +{ + // Echo the characters back to the Debugger's stdout, that way if you + // type characters while a command is running, you'll see what you've typed. + FILE *out_fh = SBDebugger::GetOutputFileHandle(); + if (out_fh) + ::fwrite (src, 1, src_len, out_fh); +} + +size_t +Driver::EditLineInputReaderCallback +( + void *baton, + SBInputReader *reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + Driver *driver = (Driver *)baton; + + switch (notification) + { + case eInputReaderActivate: + break; + + case eInputReaderReactivate: + driver->ReadyForCommand(); + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderGotToken: + write (driver->m_editline_pty.GetMasterFileDescriptor(), bytes, bytes_len); + break; + + case eInputReaderDone: + break; + } + return bytes_len; +} + +void +Driver::MainLoop () +{ + char error_str[1024]; + if (m_editline_pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, error_str, sizeof(error_str)) == false) + { + ::fprintf (stderr, "error: failed to open driver pseudo terminal : %s", error_str); + exit(1); + } + else + { + const char *driver_slave_name = m_editline_pty.GetSlaveName (error_str, sizeof(error_str)); + if (driver_slave_name == NULL) + { + ::fprintf (stderr, "error: failed to get slave name for driver pseudo terminal : %s", error_str); + exit(2); + } + else + { + m_editline_slave_fh = ::fopen (driver_slave_name, "r+"); + if (m_editline_slave_fh == NULL) + { + SBError error; + error.SetErrorToErrno(); + ::fprintf (stderr, "error: failed to get open slave for driver pseudo terminal : %s", + error.GetCString()); + exit(3); + } + + ::setbuf (m_editline_slave_fh, NULL); + } + } + + + // struct termios stdin_termios; + + if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) + atexit (reset_stdin_termios); + + ::setbuf (stdin, NULL); + ::setbuf (stdout, NULL); + + SBDebugger::SetErrorFileHandle (stderr, false); + SBDebugger::SetOutputFileHandle (stdout, false); + SBDebugger::SetInputFileHandle (stdin, true); + + // You have to drain anything that comes to the master side of the PTY. master_out_comm is + // for that purpose. The reason you need to do this is a curious reason... editline will echo + // characters to the PTY when it gets characters while el_gets is not running, and then when + // you call el_gets (or el_getc) it will try to reset the terminal back to raw mode which blocks + // if there are unconsumed characters in the out buffer. + // However, you don't need to do anything with the characters, since editline will dump these + // unconsumed characters after printing the prompt again in el_gets. + + SBCommunication master_out_comm("driver.editline"); + master_out_comm.AdoptFileDesriptor(m_editline_pty.GetMasterFileDescriptor(), false); + master_out_comm.SetReadThreadBytesReceivedCallback(Driver::MasterThreadBytesReceived, this); + + if (master_out_comm.ReadThreadStart () == false) + { + ::fprintf (stderr, "error: failed to start master out read thread"); + exit(5); + } + +// const char *crash_log = GetCrashLogFilename(); +// if (crash_log) +// { +// ParseCrashLog (crash_log); +// } +// + SBCommandInterpreter sb_interpreter = SBDebugger::GetCommandInterpreter(); + + m_io_channel_ap.reset (new IOChannel(m_editline_slave_fh, stdout, stderr, this)); + + struct winsize window_size; + if (isatty (STDIN_FILENO) + && ::ioctl (STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) + { + char buffer[25]; + + sprintf (buffer, "set term-width %d", window_size.ws_col); + SBDebugger::HandleCommand ((const char *) buffer); + } + + // Since input can be redirected by the debugger, we must insert our editline + // input reader in the queue so we know when our reader should be active + // and so we can receive bytes only when we are supposed to. + SBError err (m_editline_reader.Initialize (Driver::EditLineInputReaderCallback, // callback + this, // baton + eInputReaderGranularityByte, // token_size + NULL, // end token - NULL means never done + NULL, // prompt - taken care of elsewhere + false)); // echo input - don't need Debugger + // to do this, we handle it elsewhere + + if (err.Fail()) + { + ::fprintf (stderr, "error: %s", err.GetCString()); + exit (6); + } + + SBDebugger::PushInputReader (m_editline_reader); + + SBListener listener(SBDebugger::GetListener()); + if (listener.IsValid()) + { + + listener.StartListeningForEvents (*m_io_channel_ap, + IOChannel::eBroadcastBitHasUserInput | + IOChannel::eBroadcastBitUserInterrupt | + IOChannel::eBroadcastBitThreadShouldExit | + IOChannel::eBroadcastBitThreadDidStart | + IOChannel::eBroadcastBitThreadDidExit); + + if (m_io_channel_ap->Start ()) + { + bool iochannel_thread_exited = false; + + listener.StartListeningForEvents (sb_interpreter.GetBroadcaster(), + SBCommandInterpreter::eBroadcastBitQuitCommandReceived); + + // Before we handle any options from the command line, we parse the + // .lldbinit file in the user's home directory. + SBCommandReturnObject result; + sb_interpreter.SourceInitFileInHomeDirectory(result); + if (GetDebugMode()) + { + result.PutError (SBDebugger::GetErrorFileHandle()); + result.PutOutput (SBDebugger::GetOutputFileHandle()); + } + + // Now we handle options we got from the command line + char command_string[PATH_MAX * 2]; + const size_t num_source_command_files = GetNumSourceCommandFiles(); + if (num_source_command_files > 0) + { + for (size_t i=0; i < num_source_command_files; ++i) + { + const char *command_file = GetSourceCommandFileAtIndex(i); + ::snprintf (command_string, sizeof(command_string), "source '%s'", command_file); + SBDebugger::GetCommandInterpreter().HandleCommand (command_string, result, false); + if (GetDebugMode()) + { + result.PutError (SBDebugger::GetErrorFileHandle()); + result.PutOutput (SBDebugger::GetOutputFileHandle()); + } + } + } + + if (!m_option_data.m_filename.empty()) + { + char arch_name[64]; + if (SBDebugger::GetDefaultArchitecture (arch_name, sizeof (arch_name))) + ::snprintf (command_string, sizeof (command_string), "file --arch=%s '%s'", arch_name, + m_option_data.m_filename.c_str()); + else + ::snprintf (command_string, sizeof(command_string), "file '%s'", m_option_data.m_filename.c_str()); + + SBDebugger::HandleCommand (command_string); + } + + // Now that all option parsing is done, we try and parse the .lldbinit + // file in the current working directory + sb_interpreter.SourceInitFileInCurrentWorkingDirectory (result); + if (GetDebugMode()) + { + result.PutError(SBDebugger::GetErrorFileHandle()); + result.PutOutput(SBDebugger::GetOutputFileHandle()); + } + + SBEvent event; + + // Make sure the IO channel is started up before we try to tell it we + // are ready for input + listener.WaitForEventForBroadcasterWithType (UINT32_MAX, + *m_io_channel_ap, + IOChannel::eBroadcastBitThreadDidStart, + event); + + ReadyForCommand (); + + bool done = false; + while (!done) + { + listener.WaitForEvent (UINT32_MAX, event); + if (event.IsValid()) + { + if (event.GetBroadcaster().IsValid()) + { + uint32_t event_type = event.GetType(); + if (event.BroadcasterMatchesRef (*m_io_channel_ap)) + { + if ((event_type & IOChannel::eBroadcastBitThreadShouldExit) || + (event_type & IOChannel::eBroadcastBitThreadDidExit)) + { + done = true; + if (event_type & IOChannel::eBroadcastBitThreadDidExit) + iochannel_thread_exited = true; + break; + } + else + done = HandleIOEvent (event); + } + else if (event.BroadcasterMatchesRef (SBDebugger::GetCurrentTarget().GetProcess().GetBroadcaster())) + { + HandleProcessEvent (event); + } + else if (event.BroadcasterMatchesRef (sb_interpreter.GetBroadcaster())) + { + if (event_type & SBCommandInterpreter::eBroadcastBitQuitCommandReceived) + done = true; + } + } + } + } + + reset_stdin_termios (); + + CloseIOChannelFile (); + + if (!iochannel_thread_exited) + { + SBEvent event; + listener.GetNextEventForBroadcasterWithType (*m_io_channel_ap, + IOChannel::eBroadcastBitThreadDidExit, + event); + if (!event.IsValid()) + { + // Send end EOF to the driver file descriptor + m_io_channel_ap->Stop(); + } + } + + SBProcess process = SBDebugger::GetCurrentTarget().GetProcess(); + if (process.IsValid()) + process.Destroy(); + } + } +} + + +void +Driver::ReadyForCommand () +{ + if (m_waiting_for_command == false) + { + m_waiting_for_command = true; + BroadcastEventByType (Driver::eBroadcastBitReadyForInput, true); + } +} + + +int +main (int argc, char const *argv[]) +{ + + SBDebugger::Initialize(); + + SBHostOS::ThreadCreated ("[main]"); + + // Do a little setup on the debugger before we get going + SBDebugger::SetAsync(true); + Driver driver; + + bool valid_args = driver.ParseArgs (argc, argv, stdout, stderr); + if (valid_args) + { + driver.MainLoop (); + } + + SBDebugger::Terminate(); + return 0; +} diff --git a/lldb/tools/driver/Driver.h b/lldb/tools/driver/Driver.h new file mode 100644 index 000000000000..ad82c989f1a3 --- /dev/null +++ b/lldb/tools/driver/Driver.h @@ -0,0 +1,156 @@ +//===-- Driver.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_Driver_h_ +#define lldb_Driver_h_ + +#include "PseudoTerminal.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + + +class IOChannel; + +namespace lldb +{ + class SBInputReader; +} + + +class Driver : public lldb::SBBroadcaster +{ +public: + enum { + eBroadcastBitReadyForInput = (1 << 0), + eBroadcastBitThreadShouldExit = (1 << 1) + }; + + Driver (); + + virtual + ~Driver (); + + void + MainLoop (); + + void + PutSTDIN (const char *src, size_t src_len); + + void + GetFromMaster (const char *src, size_t src_len); + + bool + HandleIOEvent (const lldb::SBEvent &event); + + void + HandleProcessEvent (const lldb::SBEvent &event); + + bool + ParseArgs (int argc, const char *argv[], FILE *out_fh, FILE *err_fh); + + const char * + GetFilename() const; + + const char * + GetCrashLogFilename() const; + + const char * + GetArchName() const; + + lldb::ScriptLanguage + GetScriptLanguage() const; + + size_t + GetNumSourceCommandFiles () const; + + const char * + GetSourceCommandFileAtIndex (uint32_t idx) const; + + bool + GetDebugMode() const; + + + class OptionData + { + public: + OptionData (); + ~OptionData (); + + void + Clear(); + + //static lldb::OptionDefinition m_cmd_option_table[]; + + std::string m_filename; + lldb::ScriptLanguage m_script_lang; + std::string m_crash_log; + std::vector m_source_command_files; + bool m_debug_mode; + bool m_print_version; + bool m_print_help; + typedef std::set OptionSet; + OptionSet m_seen_options; + }; + + + static lldb::SBError + SetOptionValue (int option_idx, + const char *option_arg, + Driver::OptionData &data); + + +private: + lldb_utility::PseudoTerminal m_editline_pty; + FILE *m_editline_slave_fh; + lldb::SBInputReader m_editline_reader; + std::auto_ptr m_io_channel_ap; + OptionData m_option_data; + bool m_waiting_for_command; + + void + ResetOptionValues (); + + void + GetProcessSTDOUT (); + + void + GetProcessSTDERR (); + + void + UpdateCurrentThread (); + + void + CloseIOChannelFile (); + + static size_t + EditLineInputReaderCallback (void *baton, + lldb::SBInputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static void + ReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + static void + MasterThreadBytesReceived (void *baton, const void *src, size_t src_len); + + void + ReadyForCommand (); +}; + +#endif lldb_Driver_h_ diff --git a/lldb/tools/driver/IOChannel.cpp b/lldb/tools/driver/IOChannel.cpp new file mode 100644 index 000000000000..a83e1b686e62 --- /dev/null +++ b/lldb/tools/driver/IOChannel.cpp @@ -0,0 +1,449 @@ +//===-- IOChannel.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IOChannel.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace lldb; + +typedef std::map PromptMap; +const char *g_default_prompt = "(lldb) "; +PromptMap g_prompt_map; + +static const char* +el_prompt(EditLine *el) +{ + PromptMap::const_iterator pos = g_prompt_map.find (el); + if (pos == g_prompt_map.end()) + return g_default_prompt; + return pos->second.c_str(); +} + +const char * +IOChannel::GetPrompt () +{ + PromptMap::const_iterator pos = g_prompt_map.find (m_edit_line); + if (pos == g_prompt_map.end()) + return g_default_prompt; + return pos->second.c_str(); +} + +unsigned char +IOChannel::ElCompletionFn (EditLine *e, int ch) +{ + IOChannel *io_channel; + if (el_get(e, EL_CLIENTDATA, &io_channel) == 0) + { + return io_channel->HandleCompletion (e, ch); + } + else + { + return CC_ERROR; + } +} + +unsigned char +IOChannel::HandleCompletion (EditLine *e, int ch) +{ + assert (e == m_edit_line); + + const LineInfo *line_info = el_line(m_edit_line); + SBStringList completions; + size_t page_size = 40; + + int num_completions + = SBDebugger::GetCommandInterpreter().HandleCompletion (line_info->buffer, + line_info->cursor, + line_info->lastchar, + 0, + -1, + completions); + + if (num_completions == -1) + { + el_insertstr (m_edit_line, m_completion_key); + return CC_REDISPLAY; + } + + // If we get a longer match display that first. + const char *completion_str = completions.GetStringAtIndex(0); + if (completion_str != NULL && *completion_str != '\0') + { + el_insertstr (m_edit_line, completion_str); + return CC_REDISPLAY; + } + + if (num_completions > 1) + { + const char *comment = "\nAvailable completions:"; + + int num_elements = num_completions + 1; + OutWrite(comment, strlen (comment)); + if (num_completions < page_size) + { + for (int i = 1; i < num_elements; i++) + { + const char *completion_str = completions.GetStringAtIndex(i); + OutWrite("\n\t", 2); + OutWrite(completion_str, strlen (completion_str)); + } + OutWrite ("\n", 1); + } + else + { + int cur_pos = 1; + char reply; + int got_char; + while (cur_pos < num_elements) + { + int endpoint = cur_pos + page_size; + if (endpoint > num_elements) + endpoint = num_elements; + for (; cur_pos < endpoint; cur_pos++) + { + const char *completion_str = completions.GetStringAtIndex(cur_pos); + OutWrite("\n\t", 2); + OutWrite(completion_str, strlen (completion_str)); + } + + if (cur_pos >= num_elements) + { + OutWrite("\n", 1); + break; + } + + OutWrite("\nMore (Y/n/a): ", strlen ("\nMore (Y/n/a): ")); + reply = 'n'; + got_char = el_getc(m_edit_line, &reply); + if (got_char == -1 || reply == 'n') + break; + if (reply == 'a') + page_size = num_elements - cur_pos; + } + } + + } + + if (num_completions == 0) + return CC_REFRESH_BEEP; + else + return CC_REDISPLAY; +} + +IOChannel::IOChannel +( + FILE *in, + FILE *out, + FILE *err, + Driver *driver +) : + SBBroadcaster ("IOChannel"), + m_driver (driver), + m_read_thread (LLDB_INVALID_HOST_THREAD), + m_read_thread_should_exit (false), + m_out_file (out), + m_err_file (err), + m_edit_line (::el_init (SBHostOS::GetProgramFileSpec().GetFileName(), in, out, err)), + m_history (history_init()), + m_completion_key ("\t") +{ + assert (m_edit_line); + ::el_set (m_edit_line, EL_PROMPT, el_prompt); + ::el_set (m_edit_line, EL_EDITOR, "emacs"); + ::el_set (m_edit_line, EL_HIST, history, m_history); + + // Source $PWD/.editrc then $HOME/.editrc + ::el_source (m_edit_line, NULL); + + el_set(m_edit_line, EL_ADDFN, "lldb_complete", + "LLDB completion function", + IOChannel::ElCompletionFn); + el_set(m_edit_line, EL_BIND, m_completion_key, "lldb_complete", NULL); + el_set (m_edit_line, EL_CLIENTDATA, this); + + assert (m_history); + ::history (m_history, &m_history_event, H_SETSIZE, 800); + ::history (m_history, &m_history_event, H_SETUNIQUE, 1); + // Load history + HistorySaveLoad (false); +} + +IOChannel::~IOChannel () +{ + // Save history + HistorySaveLoad (true); + + if (m_history != NULL) + { + ::history_end (m_history); + m_history = NULL; + } + + if (m_edit_line != NULL) + { + ::el_end (m_edit_line); + m_edit_line = NULL; + } +} + +void +IOChannel::HistorySaveLoad (bool save) +{ + if (m_history != NULL) + { + char history_path[PATH_MAX]; + ::snprintf (history_path, sizeof(history_path), "~/.%s-history", SBHostOS::GetProgramFileSpec().GetFileName()); + if (SBFileSpec::ResolvePath (history_path, history_path, sizeof(history_path)) < sizeof(history_path) - 1) + { + const char *path_ptr = history_path; + if (save) + ::history (m_history, &m_history_event, H_SAVE, path_ptr); + else + ::history (m_history, &m_history_event, H_LOAD, path_ptr); + } + } +} + +bool +IOChannel::LibeditGetInput (std::string &new_line) +{ + if (m_edit_line != NULL) + { + int line_len = 0; + const char *line = ::el_gets (m_edit_line, &line_len); + if (line) + { + // strip any newlines off the end of the string... + while (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) + --line_len; + if (line_len > 0) + { + ::history (m_history, &m_history_event, H_ENTER, line); + new_line.assign (line, line_len); // Omit the newline + } + else + { + // Someone just hit ENTER, return the empty string + new_line.clear(); + } + // Return true to indicate success even if a string is empty + return true; + } + } + // Return false to indicate failure. This can happen when the file handle + // is closed (EOF). + new_line.clear(); + return false; +} + +void * +IOChannel::IOReadThread (void *ptr) +{ + IOChannel *myself = static_cast (ptr); + myself->Run(); + return NULL; +} + +void +IOChannel::Run () +{ + SBListener listener("IOChannel::Run"); + std::string new_line; + + SBBroadcaster interpreter_broadcaster (SBDebugger::GetCommandInterpreter().GetBroadcaster()); + listener.StartListeningForEvents (interpreter_broadcaster, + SBCommandInterpreter::eBroadcastBitResetPrompt | + SBCommandInterpreter::eBroadcastBitThreadShouldExit | + SBCommandInterpreter::eBroadcastBitQuitCommandReceived); + + listener.StartListeningForEvents (*this, + IOChannel::eBroadcastBitThreadShouldExit); + + listener.StartListeningForEvents (*m_driver, + Driver::eBroadcastBitReadyForInput | + Driver::eBroadcastBitThreadShouldExit); + + // Let anyone know that the IO channel is up and listening and ready for events + BroadcastEventByType (eBroadcastBitThreadDidStart); + bool done = false; + while (!done) + { + SBEvent event; + + listener.WaitForEvent (UINT32_MAX, event); + if (!event.IsValid()) + continue; + + const uint32_t event_type = event.GetType(); + + if (event.GetBroadcaster().IsValid()) + { + if (event.BroadcasterMatchesPtr (m_driver)) + { + if (event_type & Driver::eBroadcastBitReadyForInput) + { + std::string line; + + if (CommandQueueIsEmpty()) + { + if (LibeditGetInput(line) == false) + { + // EOF or some other file error occurred + done = true; + continue; + } + } + else + { + GetCommandFromQueue (line); + } + + // TO BE DONE: FIGURE OUT WHICH COMMANDS SHOULD NOT BE REPEATED IF USER PRESSES PLAIN 'RETURN' + // AND TAKE CARE OF THAT HERE. + + SBEvent line_event(IOChannel::eBroadcastBitHasUserInput, + line.c_str(), + line.size()); + BroadcastEvent (line_event); + } + else if (event_type & Driver::eBroadcastBitThreadShouldExit) + { + done = true; + break; + } + } + else if (event.BroadcasterMatchesRef (interpreter_broadcaster)) + { + switch (event_type) + { + case SBCommandInterpreter::eBroadcastBitResetPrompt: + { + const char *new_prompt = SBEvent::GetCStringFromEvent (event); + if (new_prompt) + g_prompt_map[m_edit_line] = new_prompt; + } + break; + + case SBCommandInterpreter::eBroadcastBitThreadShouldExit: + case SBCommandInterpreter::eBroadcastBitQuitCommandReceived: + done = true; + break; + } + } + else if (event.BroadcasterMatchesPtr (this)) + { + if (event_type & IOChannel::eBroadcastBitThreadShouldExit) + { + done = true; + break; + } + } + } + } + BroadcastEventByType (IOChannel::eBroadcastBitThreadDidExit); + m_driver = NULL; + m_read_thread = NULL; +} + +bool +IOChannel::Start () +{ + if (m_read_thread != LLDB_INVALID_HOST_THREAD) + return true; + + m_read_thread = SBHostOS::ThreadCreate ("", IOChannel::IOReadThread, this, + NULL); + + return (m_read_thread != LLDB_INVALID_HOST_THREAD); +} + +bool +IOChannel::Stop () +{ + if (m_read_thread == NULL) + return true; + + BroadcastEventByType (eBroadcastBitThreadShouldExit); + + // Don't call Host::ThreadCancel since el_gets won't respond to this + // function call -- the thread will just die and all local variables in + // IOChannel::Run() won't get destructed down which is bad since there is + // a local listener holding onto broadcasters... To ensure proper shutdown, + // a ^D (control-D) sequence (0x04) should be written to other end of the + // the "in" file handle that was passed into the contructor as closing the + // file handle doesn't seem to make el_gets() exit.... + return SBHostOS::ThreadJoin (m_read_thread, NULL, NULL); +} + +void +IOChannel::RefreshPrompt () +{ + ::el_set (m_edit_line, EL_REFRESH); +} + +void +IOChannel::OutWrite (const char *buffer, size_t len) +{ + if (len == 0) + return; + ::fwrite (buffer, 1, len, m_out_file); +} + +void +IOChannel::ErrWrite (const char *buffer, size_t len) +{ + if (len == 0) + return; + ::fwrite (buffer, 1, len, m_err_file); +} + +void +IOChannel::AddCommandToQueue (const char *command) +{ + m_command_queue.push (std::string(command)); +} + +bool +IOChannel::GetCommandFromQueue (std::string &cmd) +{ + if (m_command_queue.empty()) + return false; + cmd.swap(m_command_queue.front()); + m_command_queue.pop (); + return true; +} + +int +IOChannel::CommandQueueSize () const +{ + return m_command_queue.size(); +} + +void +IOChannel::ClearCommandQueue () +{ + while (!m_command_queue.empty()) + m_command_queue.pop(); +} + +bool +IOChannel::CommandQueueIsEmpty () const +{ + return m_command_queue.empty(); +} diff --git a/lldb/tools/driver/IOChannel.h b/lldb/tools/driver/IOChannel.h new file mode 100644 index 000000000000..dec7c82da297 --- /dev/null +++ b/lldb/tools/driver/IOChannel.h @@ -0,0 +1,113 @@ +//===-- IOChannel.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_IOChannel_h_ +#define lldb_IOChannel_h_ + +#include +#include + +#include +#include + +#include "Driver.h" + +class IOChannel : public lldb::SBBroadcaster +{ +public: + enum { + eBroadcastBitHasUserInput = (1 << 0), + eBroadcastBitUserInterrupt = (1 << 1), + eBroadcastBitThreadShouldExit = (1 << 2), + eBroadcastBitThreadDidExit = (1 << 3), + eBroadcastBitThreadDidStart = (1 << 4), + eBroadcastBitsSTDOUT = (1 << 5), + eBroadcastBitsSTDERR = (1 << 6), + eBroadcastBitsSTDIN = (1 << 7), + eAllEventBits = 0xffffffff + }; + + IOChannel (FILE *in, + FILE *out, + FILE *err, + Driver *driver = NULL); + + virtual + ~IOChannel (); + + bool + Start (); + + bool + Stop (); + + static void * + IOReadThread (void *); + + void + Run (); + + void + OutWrite (const char *buffer, size_t len); + + void + ErrWrite (const char *buffer, size_t len); + + bool + LibeditGetInput (std::string &); + + void + SetPrompt (); + + void + RefreshPrompt (); + + void + AddCommandToQueue (const char *command); + + bool + GetCommandFromQueue (std::string &cmd); + + int + CommandQueueSize () const; + + void + ClearCommandQueue (); + + bool + CommandQueueIsEmpty () const; + + const char * + GetPrompt (); + + static unsigned char ElCompletionFn (EditLine *e, int ch); + + bool + IsGettingCommand () const; + +private: + + Driver *m_driver; + lldb::thread_t m_read_thread; + bool m_read_thread_should_exit; + FILE *m_out_file; + FILE *m_err_file; + std::queue m_command_queue; + const char *m_completion_key; + + EditLine *m_edit_line; + History *m_history; + HistEvent m_history_event; + bool m_getting_command; + void + HistorySaveLoad (bool save); + unsigned char HandleCompletion (EditLine *e, int ch); +}; + +#endif // lldb_IOChannel_h_ diff --git a/lldb/tools/driver/lldb-Info.plist b/lldb/tools/driver/lldb-Info.plist new file mode 100644 index 000000000000..7c1bfc734a7f --- /dev/null +++ b/lldb/tools/driver/lldb-Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.lldb + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + lldb + CFBundleVersion + 2 + SecTaskAccess + + allowed + debug + + + diff --git a/lldb/www/content.css b/lldb/www/content.css new file mode 100644 index 000000000000..ab6983b48491 --- /dev/null +++ b/lldb/www/content.css @@ -0,0 +1,25 @@ +html, body { + padding:0px; + font-size:small; font-family:"Lucida Grande", "Lucida Sans Unicode", Arial, Verdana, Helvetica, sans-serif; background-color: #fff; color: #222; + line-height:1.5; +} + +h1, h2, h3, tt { color: #000 } + +h1 { padding-top:0px; margin-top:0px;} +h2 { color:#333333; padding-top:0.5em; } +h3 { padding-top: 0.5em; margin-bottom: -0.25em; color:#2d58b7} +li { padding-bottom: 0.5em; } +ul { padding-left:1.5em; } + +/* Slides */ +IMG.img_slide { + display: block; + margin-left: auto; + margin-right: auto +} + +.itemTitle { color:#2d58b7 } + +/* Tables */ +tr { vertical-align:top } diff --git a/lldb/www/index.html b/lldb/www/index.html new file mode 100644 index 000000000000..00bebe1780ce --- /dev/null +++ b/lldb/www/index.html @@ -0,0 +1,197 @@ + + + + + + The LLDB Debugger + + + + + + +
+ +

The LLDB Debugger

+ + +

LLDB is a next generation, high-performance debugger. It is built as a set + of reusable components which highly leverage existing libraries in the + larger LLVM Project, such as the Clang expression parser and LLVM + disassembler.

+

LLDB is in early development, but is mature enough to support basic + debugging scenarios on Mac OS X in C, Objective-C and C++.

+ +

All of the code in the LLDB project is available under the standard + LLVM + License, an open source "BSD-style" license.

+ + +

Goals

+ + +

The current state of the art in open source debuggers are that + they work in the common cases for C applications, but don't + handle many "hard cases" properly. For example, C++ expression + parsing, handling overloading, templates, multi-threading, and + other non-trivial scenarios all work in some base cases, but + don't work reliably.

+ +

The goal of LLDB is to provide an amazing debugging experience that "just + works". We aim to solve these long-standing problems where debuggers get + confused, so that you can think about debugging your problem, not + about deficiencies in the debugger.

+ +

With a long view, there is no good reason for a debugger to + reinvent its own C/C++ parser, type system, know all the + target calling convention details, implement its own disassembler, + etc. By using the existing libraries vended by the LLVM + project, we believe that many of these problems will be defined + away, and the debugger can focus on important issues like + process control, efficient symbol reading and indexing, thread + management, and other debugger-specific problems.

+ +

Some more specific goals include:

+ +
    +
  • Build libraries for inclusion in IDEs, command line tools, and + other analysis tools
  • +
  • High performance and efficient memory use
  • +
  • Extensible: Python scriptable and use a plug-in architecture
  • +
  • Reuse existing compiler technology where it makes sense
  • +
  • Excellent multi-threaded debugging support
  • +
  • Great support for C, Objective-C and C++
  • +
  • Retargetable to support multiple platforms
  • +
  • Provide a base for debugger research and other innovation
  • +
+ + +

Why a new debugger?

+ + +

In order to achieve our goals we decided to start with a fresh architecture + that would support modern multi-threaded programs, handle debugging symbols + in an efficient manner, use compiler based code knowledge and have plug-in + support for functionality and extensions. Additionally we want the debugger + capabilities to be available to other analysis tools, be they scripts or + compiled programs, without requiring them to be GPL.

+ + +

Features

+ + +

LLDB supports a broad variety of basic debugging features such as + reading DWARF, supporting step, next, finish, backtraces, etc. Some + more interested bits are:

+ +
+ + +

Platform Support

+ + +

LLDB is known to work on the following platforms, but ports to new + platforms are welcome:

+ +
  • Machine Architectures: +
      +
    • Mac OS X i386 and X86-64
    • +
  • + + +

    Current Status

    + + +

    LLDB is in early development and supports basic debugging scenarios on + Mac OS X. The public API has not been finalized, and different parts are + at different levels of maturity. We welcome any help fleshing out missing + pieces and improving the code.

    + +

    What works well:

    +
      +
    • Process control, including external process control via debugserver + (which is included as part of the lldb project)
    • +
    • Breakpoints: Source-line, symbolic, C++ mangled names, module + scoping
    • +
    • Symbol reading and object file introspection
    • +
    • Script bridging
    • +
    • Thread inspection and stepping
    • +
    • Disassembly of i386, x86_64, & ARM/Thumb machine code, and + backtracing on i386 & x86_64
    • +
    • The basic command line prompt system, shared library tracking, + source listings.
    • +
    + +

    What is still pretty new:

    +
      +
    • The public API to the library
    • +
    • Expression evaluation
    • +
    • Objective-C support: stepping into/over, printing the description of + an object ("po")
    • +
    • Breakpoint actions & scripts
    • +
    • Attaching to existing processes
    • +
    + +

    What isn't there yet:

    +
      +
    • Regression test suite
    • +
    • Operating system support hasn't been fully modularized yet
    • +
    • Blocks support
    • +
    • Calling functions in expressions
    • +
    • Objective-C 2.0 Support: Printing properties, synthetic properties, + Objective-C expressions, KVO, dynamic types, dot syntax, runtime data
    • +
    • C++ support: Method access, handling demangled names, dynamic types
    • +
    • Exception support: Breaking by name, thrown object, thrower
    • + +
    + + +

    Get it and get involved!

    + + +

    To check out the code, use:

    + +
      +
    • svn co http://llvm.org/svn/llvm-project/lldb/trunk lldb
    • +
    + +

    Note that LLDB currently only builds out of the box on Darwin with + Xcode, but patches to improve portability are definitely welcome.

    + +

    Discussions about LLDB should go to the lldb-dev mailing + list. Commit messages for the lldb SVN module are automatically sent to the + lldb-commits + mailing list, and this is also the preferred mailing list for patch + submissions.

    +
    + + diff --git a/lldb/www/menu.css b/lldb/www/menu.css new file mode 100644 index 000000000000..6e96a457ab53 --- /dev/null +++ b/lldb/www/menu.css @@ -0,0 +1,39 @@ +/***************/ +/* page layout */ +/***************/ + +[id=menu] { + position:fixed; + width:25ex; +} +[id=content] { + /* ***** EDIT THIS VALUE IF CONTENT OVERLAPS MENU ***** */ + position:absolute; + left:29ex; + padding-right:4ex; +} + +/**************/ +/* menu style */ +/**************/ + +#menu .submenu { + padding-top:1em; + display:block; +} + +#menu label { + display:block; + font-weight: bold; + text-align: center; + background-color: rgb(192,192,192); +} +#menu a { + padding:0 .2em; + display:block; + text-align: center; + background-color: rgb(235,235,235); +} +#menu a:visited { + color:rgb(100,50,100); +} \ No newline at end of file diff --git a/lldb/www/menu.html.incl b/lldb/www/menu.html.incl new file mode 100644 index 000000000000..ed104c0c30a7 --- /dev/null +++ b/lldb/www/menu.html.incl @@ -0,0 +1,20 @@ + +